@dxos/react-ui-editor 0.8.2-main.f11618f → 0.8.2-main.fbd8ed0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/dist/lib/browser/index.mjs +1662 -1357
  2. package/dist/lib/browser/index.mjs.map +4 -4
  3. package/dist/lib/browser/meta.json +1 -1
  4. package/dist/lib/node/index.cjs +2175 -1872
  5. package/dist/lib/node/index.cjs.map +4 -4
  6. package/dist/lib/node/meta.json +1 -1
  7. package/dist/lib/node-esm/index.mjs +1662 -1357
  8. package/dist/lib/node-esm/index.mjs.map +4 -4
  9. package/dist/lib/node-esm/meta.json +1 -1
  10. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts +1 -1
  11. package/dist/types/src/components/EditorToolbar/EditorToolbar.d.ts.map +1 -1
  12. package/dist/types/src/{stories/InputMode.stories.d.ts → components/EditorToolbar/EditorToolbar.stories.d.ts} +3 -7
  13. package/dist/types/src/components/EditorToolbar/EditorToolbar.stories.d.ts.map +1 -0
  14. package/dist/types/src/components/EditorToolbar/blocks.d.ts +4 -3
  15. package/dist/types/src/components/EditorToolbar/blocks.d.ts.map +1 -1
  16. package/dist/types/src/components/EditorToolbar/comment.d.ts +4 -3
  17. package/dist/types/src/components/EditorToolbar/comment.d.ts.map +1 -1
  18. package/dist/types/src/components/EditorToolbar/formatting.d.ts +4 -3
  19. package/dist/types/src/components/EditorToolbar/formatting.d.ts.map +1 -1
  20. package/dist/types/src/components/EditorToolbar/headings.d.ts +4 -3
  21. package/dist/types/src/components/EditorToolbar/headings.d.ts.map +1 -1
  22. package/dist/types/src/components/EditorToolbar/image.d.ts +16 -0
  23. package/dist/types/src/components/EditorToolbar/image.d.ts.map +1 -0
  24. package/dist/types/src/components/EditorToolbar/lists.d.ts +4 -3
  25. package/dist/types/src/components/EditorToolbar/lists.d.ts.map +1 -1
  26. package/dist/types/src/components/EditorToolbar/search.d.ts +17 -0
  27. package/dist/types/src/components/EditorToolbar/search.d.ts.map +1 -0
  28. package/dist/types/src/components/EditorToolbar/util.d.ts +11 -17
  29. package/dist/types/src/components/EditorToolbar/util.d.ts.map +1 -1
  30. package/dist/types/src/components/EditorToolbar/view-mode.d.ts +4 -3
  31. package/dist/types/src/components/EditorToolbar/view-mode.d.ts.map +1 -1
  32. package/dist/types/src/defaults.d.ts.map +1 -1
  33. package/dist/types/src/extensions/annotations.d.ts.map +1 -1
  34. package/dist/types/src/extensions/autocomplete.d.ts.map +1 -1
  35. package/dist/types/src/extensions/automerge/automerge.d.ts.map +1 -1
  36. package/dist/types/src/extensions/automerge/automerge.stories.d.ts.map +1 -1
  37. package/dist/types/src/extensions/automerge/cursor.d.ts.map +1 -1
  38. package/dist/types/src/extensions/automerge/defs.d.ts +1 -1
  39. package/dist/types/src/extensions/automerge/defs.d.ts.map +1 -1
  40. package/dist/types/src/extensions/automerge/sync.d.ts.map +1 -1
  41. package/dist/types/src/extensions/automerge/update-automerge.d.ts +1 -1
  42. package/dist/types/src/extensions/automerge/update-automerge.d.ts.map +1 -1
  43. package/dist/types/src/extensions/automerge/update-codemirror.d.ts +1 -1
  44. package/dist/types/src/extensions/automerge/update-codemirror.d.ts.map +1 -1
  45. package/dist/types/src/extensions/awareness/awareness.d.ts.map +1 -1
  46. package/dist/types/src/extensions/blast.d.ts.map +1 -1
  47. package/dist/types/src/extensions/command/command.d.ts.map +1 -1
  48. package/dist/types/src/extensions/command/hint.d.ts.map +1 -1
  49. package/dist/types/src/extensions/command/menu.d.ts.map +1 -1
  50. package/dist/types/src/extensions/comments.d.ts.map +1 -1
  51. package/dist/types/src/extensions/debug.d.ts.map +1 -1
  52. package/dist/types/src/extensions/dnd.d.ts.map +1 -1
  53. package/dist/types/src/extensions/factories.d.ts.map +1 -1
  54. package/dist/types/src/extensions/folding.d.ts.map +1 -1
  55. package/dist/types/src/extensions/listener.d.ts.map +1 -1
  56. package/dist/types/src/extensions/markdown/bundle.d.ts.map +1 -1
  57. package/dist/types/src/extensions/markdown/changes.d.ts.map +1 -1
  58. package/dist/types/src/extensions/markdown/debug.d.ts.map +1 -1
  59. package/dist/types/src/extensions/markdown/decorate.d.ts +1 -0
  60. package/dist/types/src/extensions/markdown/decorate.d.ts.map +1 -1
  61. package/dist/types/src/extensions/markdown/formatting.d.ts +1 -1
  62. package/dist/types/src/extensions/markdown/formatting.d.ts.map +1 -1
  63. package/dist/types/src/extensions/markdown/highlight.d.ts.map +1 -1
  64. package/dist/types/src/extensions/markdown/image.d.ts.map +1 -1
  65. package/dist/types/src/extensions/markdown/index.d.ts +1 -0
  66. package/dist/types/src/extensions/markdown/index.d.ts.map +1 -1
  67. package/dist/types/src/extensions/markdown/link.d.ts.map +1 -1
  68. package/dist/types/src/extensions/markdown/outliner.d.ts +12 -0
  69. package/dist/types/src/extensions/markdown/outliner.d.ts.map +1 -0
  70. package/dist/types/src/extensions/markdown/table.d.ts.map +1 -1
  71. package/dist/types/src/extensions/mention.d.ts.map +1 -1
  72. package/dist/types/src/extensions/modes.d.ts +5 -5
  73. package/dist/types/src/extensions/modes.d.ts.map +1 -1
  74. package/dist/types/src/extensions/preview/preview.d.ts.map +1 -1
  75. package/dist/types/src/extensions/selection.d.ts.map +1 -1
  76. package/dist/types/src/extensions/typewriter.d.ts.map +1 -1
  77. package/dist/types/src/hooks/index.d.ts +0 -1
  78. package/dist/types/src/hooks/index.d.ts.map +1 -1
  79. package/dist/types/src/hooks/useTextEditor.d.ts.map +1 -1
  80. package/dist/types/src/stories/TextEditorBasic.stories.d.ts +3 -0
  81. package/dist/types/src/stories/TextEditorBasic.stories.d.ts.map +1 -1
  82. package/dist/types/src/stories/story-utils.d.ts.map +1 -1
  83. package/dist/types/src/styles/theme.d.ts.map +1 -1
  84. package/dist/types/src/util/cursor.d.ts.map +1 -1
  85. package/dist/types/src/util/debug.d.ts.map +1 -1
  86. package/dist/types/src/util/dom.d.ts.map +1 -1
  87. package/dist/types/src/util/facet.d.ts.map +1 -1
  88. package/dist/types/src/util/react.d.ts.map +1 -1
  89. package/dist/types/tsconfig.tsbuildinfo +1 -1
  90. package/package.json +32 -28
  91. package/src/components/EditorToolbar/EditorToolbar.stories.tsx +90 -0
  92. package/src/components/EditorToolbar/EditorToolbar.tsx +31 -32
  93. package/src/components/EditorToolbar/blocks.ts +27 -6
  94. package/src/components/EditorToolbar/comment.ts +11 -4
  95. package/src/components/EditorToolbar/formatting.ts +34 -7
  96. package/src/components/EditorToolbar/headings.ts +9 -8
  97. package/src/components/EditorToolbar/image.ts +16 -0
  98. package/src/components/EditorToolbar/lists.ts +26 -7
  99. package/src/components/EditorToolbar/search.ts +19 -0
  100. package/src/components/EditorToolbar/util.ts +14 -14
  101. package/src/components/EditorToolbar/view-mode.ts +9 -8
  102. package/src/defaults.ts +1 -1
  103. package/src/extensions/automerge/automerge.stories.tsx +9 -7
  104. package/src/extensions/automerge/automerge.test.tsx +4 -4
  105. package/src/extensions/automerge/automerge.ts +2 -2
  106. package/src/extensions/automerge/defs.ts +1 -2
  107. package/src/extensions/automerge/sync.ts +4 -4
  108. package/src/extensions/automerge/update-automerge.ts +1 -1
  109. package/src/extensions/automerge/update-codemirror.ts +3 -4
  110. package/src/extensions/markdown/changes.ts +3 -2
  111. package/src/extensions/markdown/decorate.ts +8 -7
  112. package/src/extensions/markdown/formatting.ts +4 -4
  113. package/src/extensions/markdown/index.ts +1 -0
  114. package/src/extensions/markdown/outliner.ts +235 -0
  115. package/src/extensions/markdown/styles.ts +2 -2
  116. package/src/extensions/modes.ts +5 -6
  117. package/src/hooks/index.ts +0 -1
  118. package/src/stories/TextEditorBasic.stories.tsx +44 -0
  119. package/src/stories/story-utils.tsx +7 -9
  120. package/src/styles/theme.ts +3 -0
  121. package/dist/types/src/hooks/useActionHandler.d.ts +0 -4
  122. package/dist/types/src/hooks/useActionHandler.d.ts.map +0 -1
  123. package/dist/types/src/stories/InputMode.stories.d.ts.map +0 -1
  124. package/src/hooks/useActionHandler.ts +0 -12
  125. package/src/stories/InputMode.stories.tsx +0 -124
@@ -34,13 +34,13 @@ var translations_default = [
34
34
  ];
35
35
 
36
36
  // packages/ui/react-ui-editor/src/index.ts
37
- import { EditorState as EditorState3 } from "@codemirror/state";
38
- import { EditorView as EditorView22, keymap as keymap11 } from "@codemirror/view";
37
+ import { EditorState as EditorState4 } from "@codemirror/state";
38
+ import { EditorView as EditorView23, keymap as keymap11 } from "@codemirror/view";
39
39
  import { tags as tags2 } from "@lezer/highlight";
40
40
  import { TextKind } from "@dxos/protocols/proto/dxos/echo/model/text";
41
41
 
42
42
  // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
43
- import React, { useCallback } from "react";
43
+ import React3, { memo, useCallback } from "react";
44
44
  import { ElevationProvider } from "@dxos/react-ui";
45
45
  import { MenuProvider, ToolbarMenu, createGapSeparator, useMenuActions } from "@dxos/react-ui-menu";
46
46
  import { textBlockWidth } from "@dxos/react-ui-theme";
@@ -52,692 +52,110 @@ import { createMenuAction, createMenuItemGroup } from "@dxos/react-ui-menu";
52
52
  var useEditorToolbarState = (initialState = {}) => {
53
53
  return useMemo(() => live(initialState), []);
54
54
  };
55
- var createEditorAction = (payload, icon, label = [
56
- `${payload.type} label`,
57
- {
58
- ns: translationKey
59
- }
60
- ], id = payload.type) => createMenuAction(id, {
61
- icon,
62
- label,
63
- ...payload
64
- });
55
+ var createEditorAction = (id, invoke, properties) => {
56
+ const { label = [
57
+ `${id} label`,
58
+ {
59
+ ns: translationKey
60
+ }
61
+ ], ...rest } = properties;
62
+ return createMenuAction(id, invoke, {
63
+ label,
64
+ ...rest
65
+ });
66
+ };
65
67
  var createEditorActionGroup = (id, props, icon) => createMenuItemGroup(id, {
66
68
  icon,
67
69
  iconOnly: true,
68
70
  ...props
69
71
  });
70
- var editorToolbarSearch = createEditorAction({
71
- type: "search"
72
- }, "ph--magnifying-glass--regular");
73
72
 
74
- // packages/ui/react-ui-editor/src/components/EditorToolbar/blocks.ts
75
- var createBlockGroupAction = (value) => createEditorActionGroup("block", {
76
- variant: "toggleGroup",
77
- selectCardinality: "single",
78
- value
79
- });
80
- var createBlockActions = (value, blankLine) => Object.entries({
81
- blockquote: "ph--quotes--regular",
82
- codeblock: "ph--code-block--regular",
83
- table: "ph--table--regular"
84
- }).map(([type, icon]) => {
85
- return createEditorAction({
86
- type,
87
- checked: type === value,
88
- ...type === "table" && {
89
- disabled: !!blankLine
90
- }
91
- }, icon);
92
- });
93
- var createBlocks = (state) => {
94
- const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
95
- const blockGroupAction = createBlockGroupAction(value);
96
- const blockActions = createBlockActions(value, state.blankLine);
97
- return {
98
- nodes: [
99
- blockGroupAction,
100
- ...blockActions
101
- ],
102
- edges: [
103
- {
104
- source: "root",
105
- target: "block"
106
- },
107
- ...blockActions.map(({ id }) => ({
108
- source: blockGroupAction.id,
109
- target: id
110
- }))
111
- ]
112
- };
113
- };
73
+ // packages/ui/react-ui-editor/src/extensions/annotations.ts
74
+ import { StateField } from "@codemirror/state";
75
+ import { Decoration, EditorView } from "@codemirror/view";
76
+ import { isNotFalsy } from "@dxos/util";
114
77
 
115
- // packages/ui/react-ui-editor/src/components/EditorToolbar/comment.ts
116
- var commentLabel = (comment, selection) => comment ? "selection overlaps existing comment label" : selection === false ? "select text to comment label" : "comment label";
117
- var createCommentAction = (label) => createEditorAction({
118
- type: "comment",
119
- testId: "editor.toolbar.comment"
120
- }, "ph--chat-text--regular", label);
121
- var createComment = (state) => ({
122
- nodes: [
123
- createCommentAction([
124
- commentLabel(state.comment, state.selection),
125
- {
126
- ns: translationKey
127
- }
128
- ])
129
- ],
130
- edges: [
131
- {
132
- source: "root",
133
- target: "comment"
134
- }
135
- ]
78
+ // packages/ui/react-ui-editor/src/util/facet.ts
79
+ import { Facet } from "@codemirror/state";
80
+ var singleValueFacet = (defaultValue) => Facet.define({
81
+ // Called immediately.
82
+ combine: (providers) => {
83
+ return providers[0] ?? defaultValue;
84
+ }
136
85
  });
137
86
 
138
- // packages/ui/react-ui-editor/src/components/EditorToolbar/formatting.ts
139
- var formats = {
140
- strong: "ph--text-b--regular",
141
- emphasis: "ph--text-italic--regular",
142
- strikethrough: "ph--text-strikethrough--regular",
143
- code: "ph--code--regular",
144
- link: "ph--link--regular"
87
+ // packages/ui/react-ui-editor/src/util/cursor.ts
88
+ var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
89
+ var defaultCursorConverter = {
90
+ toCursor: (position) => position.toString(),
91
+ fromCursor: (cursor) => parseInt(cursor)
145
92
  };
146
- var createFormattingGroup = (formatting) => createEditorActionGroup("formatting", {
147
- variant: "toggleGroup",
148
- selectCardinality: "multiple",
149
- value: Object.keys(formats).filter((key) => !!formatting[key])
150
- });
151
- var createFormattingActions = (formatting) => Object.entries(formats).map(([type, icon]) => createEditorAction({
152
- type,
153
- checked: !!formatting[type]
154
- }, icon));
155
- var createFormatting = (state) => {
156
- const formattingGroupAction = createFormattingGroup(state);
157
- const formattingActions = createFormattingActions(state);
158
- return {
159
- nodes: [
160
- formattingGroupAction,
161
- ...formattingActions
162
- ],
163
- edges: [
164
- {
165
- source: "root",
166
- target: "formatting"
167
- },
168
- ...formattingActions.map(({ id }) => ({
169
- source: formattingGroupAction.id,
170
- target: id
171
- }))
172
- ]
173
- };
93
+ var Cursor = class _Cursor {
94
+ static {
95
+ this.converter = singleValueFacet(defaultCursorConverter);
96
+ }
97
+ static {
98
+ this.getCursorFromRange = (state, range) => {
99
+ const cursorConverter2 = state.facet(_Cursor.converter);
100
+ const from = cursorConverter2.toCursor(range.from);
101
+ const to = cursorConverter2.toCursor(range.to, -1);
102
+ return [
103
+ from,
104
+ to
105
+ ].join(":");
106
+ };
107
+ }
108
+ static {
109
+ this.getRangeFromCursor = (state, cursor) => {
110
+ const cursorConverter2 = state.facet(_Cursor.converter);
111
+ const parts = cursor.split(":");
112
+ const from = cursorConverter2.fromCursor(parts[0]);
113
+ const to = cursorConverter2.fromCursor(parts[1]);
114
+ return from !== void 0 && to !== void 0 ? {
115
+ from,
116
+ to
117
+ } : void 0;
118
+ };
119
+ }
174
120
  };
175
121
 
176
- // packages/ui/react-ui-editor/src/components/EditorToolbar/headings.ts
177
- var createHeadingGroupAction = (value) => createEditorActionGroup("heading", {
178
- variant: "dropdownMenu",
179
- applyActive: true,
180
- selectCardinality: "single",
181
- value
182
- }, "ph--text-h--regular");
183
- var createHeadingActions = (value) => Object.entries({
184
- "0": "ph--paragraph--regular",
185
- "1": "ph--text-h-one--regular",
186
- "2": "ph--text-h-two--regular",
187
- "3": "ph--text-h-three--regular",
188
- "4": "ph--text-h-four--regular",
189
- "5": "ph--text-h-five--regular",
190
- "6": "ph--text-h-six--regular"
191
- }).map(([levelStr, icon]) => {
192
- const level = parseInt(levelStr);
193
- return createEditorAction({
194
- type: "heading",
195
- data: level,
196
- checked: value === levelStr
197
- }, icon, [
198
- "heading level label",
199
- {
200
- count: level,
201
- ns: translationKey
122
+ // packages/ui/react-ui-editor/src/util/debug.ts
123
+ import { log } from "@dxos/log";
124
+ var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
125
+ var wrapWithCatch = (fn) => {
126
+ return (...args) => {
127
+ try {
128
+ return fn(...args);
129
+ } catch (err) {
130
+ log.catch(err, void 0, {
131
+ F: __dxlog_file,
132
+ L: 15,
133
+ S: void 0,
134
+ C: (f, a) => f(...a)
135
+ });
202
136
  }
203
- ], `heading--${levelStr}`);
204
- });
205
- var computeHeadingValue = (state) => {
206
- const blockType = state ? state.blockType : "paragraph";
207
- const header = blockType && /heading(\d)/.exec(blockType);
208
- return header ? header[1] : blockType === "paragraph" || !blockType ? "0" : "";
209
- };
210
- var createHeadings = (state) => {
211
- const headingValue = computeHeadingValue(state);
212
- const headingGroupAction = createHeadingGroupAction(headingValue);
213
- const headingActions = createHeadingActions(headingValue);
214
- return {
215
- nodes: [
216
- headingGroupAction,
217
- ...headingActions
218
- ],
219
- edges: [
220
- {
221
- source: "root",
222
- target: "heading"
223
- },
224
- ...headingActions.map(({ id }) => ({
225
- source: headingGroupAction.id,
226
- target: id
227
- }))
228
- ]
229
137
  };
230
138
  };
231
-
232
- // packages/ui/react-ui-editor/src/components/EditorToolbar/lists.ts
233
- var listStyles = {
234
- bullet: "ph--list-bullets--regular",
235
- ordered: "ph--list-numbers--regular",
236
- task: "ph--list-checks--regular"
139
+ var callbackWrapper = (fn) => (...args) => {
140
+ try {
141
+ return fn(...args);
142
+ } catch (err) {
143
+ log.catch(err, void 0, {
144
+ F: __dxlog_file,
145
+ L: 29,
146
+ S: void 0,
147
+ C: (f, a) => f(...a)
148
+ });
149
+ }
237
150
  };
238
- var createListGroupAction = (value) => createEditorActionGroup("list", {
239
- variant: "toggleGroup",
240
- selectCardinality: "single",
241
- value
242
- });
243
- var createListActions = (value) => Object.entries(listStyles).map(([listStyle, icon]) => createEditorAction({
244
- type: `list-${listStyle}`,
245
- checked: value === listStyle
246
- }, icon));
247
- var createLists = (state) => {
248
- const value = state.listStyle ?? "";
249
- const listGroupAction = createListGroupAction(value);
250
- const listActionsMap = createListActions(value);
251
- return {
252
- nodes: [
253
- listGroupAction,
254
- ...listActionsMap
255
- ],
256
- edges: [
257
- {
258
- source: "root",
259
- target: "list"
260
- },
261
- ...listActionsMap.map(({ id }) => ({
262
- source: listGroupAction.id,
263
- target: id
264
- }))
265
- ]
266
- };
151
+ var debugDispatcher = (trs, view) => {
152
+ logChanges(trs);
153
+ view.update(trs);
267
154
  };
268
-
269
- // packages/ui/react-ui-editor/src/components/EditorToolbar/view-mode.ts
270
- var createViewModeGroupAction = (value) => createEditorActionGroup("viewMode", {
271
- variant: "dropdownMenu",
272
- applyActive: true,
273
- selectCardinality: "single",
274
- value
275
- }, "ph--eye--regular");
276
- var createViewModeActions = (value) => Object.entries({
277
- preview: "ph--eye--regular",
278
- source: "ph--pencil-simple--regular",
279
- readonly: "ph--pencil-slash--regular"
280
- }).map(([viewMode, icon]) => {
281
- return createEditorAction({
282
- type: "view-mode",
283
- data: viewMode,
284
- checked: viewMode === value
285
- }, icon, [
286
- `${viewMode} mode label`,
287
- {
288
- ns: translationKey
289
- }
290
- ], `view-mode--${viewMode}`);
291
- });
292
- var createViewMode = (state) => {
293
- const value = state.viewMode ?? "source";
294
- const viewModeGroupAction = createViewModeGroupAction(value);
295
- const viewModeActions = createViewModeActions(value);
296
- return {
297
- nodes: [
298
- viewModeGroupAction,
299
- ...viewModeActions
300
- ],
301
- edges: [
302
- {
303
- source: "root",
304
- target: "viewMode"
305
- },
306
- ...viewModeActions.map(({ id }) => ({
307
- source: viewModeGroupAction.id,
308
- target: id
309
- }))
310
- ]
311
- };
312
- };
313
-
314
- // packages/ui/react-ui-editor/src/defaults.ts
315
- import { EditorView } from "@codemirror/view";
316
- import { mx as mx2 } from "@dxos/react-ui-theme";
317
-
318
- // packages/ui/react-ui-editor/src/styles/markdown.ts
319
- import { mx } from "@dxos/react-ui-theme";
320
- var headings = {
321
- 1: "text-4xl",
322
- 2: "text-3xl",
323
- 3: "text-2xl",
324
- 4: "text-xl",
325
- 5: "text-lg",
326
- 6: ""
327
- };
328
- var theme = {
329
- code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
330
- codeMark: "font-mono text-primary-500",
331
- mark: "opacity-50",
332
- heading: (level) => {
333
- return mx(headings[level], "dark:text-primary-400");
334
- }
335
- };
336
-
337
- // packages/ui/react-ui-editor/src/styles/tokens.ts
338
- import get from "lodash.get";
339
- import { tokens } from "@dxos/react-ui-theme";
340
- var getToken = (path, defaultValue) => {
341
- const value = get(tokens, path, defaultValue);
342
- return value?.toString() ?? "";
343
- };
344
- var fontBody = getToken("fontFamily.body");
345
- var fontMono = getToken("fontFamily.mono");
346
-
347
- // packages/ui/react-ui-editor/src/styles/theme.ts
348
- var defaultTheme = {
349
- "&": {},
350
- "&.cm-focused": {
351
- outline: "none"
352
- },
353
- /**
354
- * Scroller
355
- */
356
- ".cm-scroller": {
357
- overflowY: "auto"
358
- },
359
- /**
360
- * Content
361
- * NOTE: Apply margins to content so that scrollbar is at the edge of the container.
362
- */
363
- ".cm-content": {
364
- padding: "unset",
365
- fontFamily: fontBody,
366
- // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
367
- fontSize: "16px",
368
- lineHeight: 1.5,
369
- color: "unset"
370
- },
371
- /**
372
- * Gutters
373
- * NOTE: Gutters should have the same top margin as the content.
374
- */
375
- ".cm-gutters": {
376
- borderRight: "none",
377
- background: "transparent"
378
- },
379
- ".cm-gutter": {},
380
- ".cm-gutter.cm-lineNumbers": {
381
- paddingRight: "4px",
382
- borderRight: "1px solid var(--dx-separator)"
383
- },
384
- ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
385
- minWidth: "40px",
386
- alignContent: "center"
387
- },
388
- /**
389
- * Height is set to match the corresponding line.
390
- */
391
- ".cm-gutterElement": {
392
- alignItems: "center",
393
- fontSize: "12px"
394
- },
395
- /**
396
- * Line.
397
- */
398
- ".cm-line": {
399
- paddingInline: 0
400
- },
401
- ".cm-activeLine": {
402
- background: "var(--dx-cmActiveLine)"
403
- },
404
- /**
405
- * Cursor (layer).
406
- */
407
- ".cm-cursor, .cm-dropCursor": {
408
- borderLeft: "2px solid var(--dx-cmCursor)"
409
- },
410
- ".cm-placeholder": {
411
- color: "var(--dx-subdued)"
412
- },
413
- /**
414
- * Selection (layer).
415
- */
416
- ".cm-selectionBackground": {
417
- background: "var(--dx-cmSelection)"
418
- },
419
- /**
420
- * Search.
421
- * NOTE: Matches comment.
422
- */
423
- ".cm-searchMatch": {
424
- margin: "0 -3px",
425
- padding: "3px",
426
- borderRadius: "3px",
427
- background: "var(--dx-cmHighlightSurface)",
428
- color: "var(--dx-cmHighlight)"
429
- },
430
- ".cm-searchMatch-selected": {
431
- textDecoration: "underline"
432
- },
433
- /**
434
- * Link.
435
- */
436
- ".cm-link": {
437
- textDecorationLine: "underline",
438
- textDecorationThickness: "1px",
439
- textDecorationColor: "var(--dx-separator)",
440
- textUnderlineOffset: "2px",
441
- borderRadius: ".125rem"
442
- },
443
- ".cm-link > span": {
444
- color: "var(--dx-accentText)"
445
- },
446
- /**
447
- * Tooltip.
448
- */
449
- ".cm-tooltip": {
450
- background: "var(--dx-baseSurface)"
451
- },
452
- ".cm-tooltip-below": {},
453
- /**
454
- * Autocomplete.
455
- * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
456
- */
457
- ".cm-tooltip.cm-tooltip-autocomplete": {
458
- marginTop: "4px",
459
- marginLeft: "-3px"
460
- },
461
- ".cm-tooltip.cm-tooltip-autocomplete > ul": {
462
- maxHeight: "20em"
463
- },
464
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
465
- ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
466
- ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
467
- paddingLeft: "4px !important",
468
- borderBottom: "none !important",
469
- color: "var(--dx-accentText)"
470
- },
471
- ".cm-tooltip.cm-completionInfo": {
472
- width: "360px !important",
473
- margin: "-10px 1px 0 1px",
474
- padding: "8px !important",
475
- borderColor: "var(--dx-separator)"
476
- },
477
- ".cm-completionIcon": {
478
- display: "none"
479
- },
480
- ".cm-completionLabel": {
481
- fontFamily: fontBody
482
- },
483
- ".cm-completionMatchedText": {
484
- textDecoration: "none !important",
485
- opacity: 0.5
486
- },
487
- /**
488
- * Panels
489
- * https://github.com/codemirror/search/blob/main/src/search.ts#L745
490
- *
491
- * Find/replace panel.
492
- * <div class="cm-announced">...</div>
493
- * <div class="cm-scroller">...</div>
494
- * <div class="cm-panels cm-panels-bottom">
495
- * <div class="cm-search cm-panel">
496
- * <input class="cm-textfield" />
497
- * <button class="cm-button">...</button>
498
- * <label><input type="checkbox" />...</label>
499
- * </div>
500
- * </div
501
- */
502
- // TODO(burdon): Implement custom panel (with icon buttons).
503
- ".cm-panels": {},
504
- ".cm-panel": {
505
- fontFamily: fontBody,
506
- backgroundColor: "var(--surface-bg)"
507
- },
508
- ".cm-panel input, .cm-panel button, .cm-panel label": {
509
- color: "var(--dx-subdued)",
510
- fontFamily: fontBody,
511
- fontSize: "14px",
512
- all: "unset",
513
- margin: "3px !important",
514
- padding: "2px 6px !important",
515
- outline: "1px solid transparent"
516
- },
517
- ".cm-panel input, .cm-panel button": {
518
- backgroundColor: "var(--dx-input)"
519
- },
520
- ".cm-panel input:focus, .cm-panel button:focus": {
521
- outline: "1px solid var(--dx-accentFocusIndicator)"
522
- },
523
- ".cm-panel label": {
524
- display: "inline-flex",
525
- alignItems: "center",
526
- cursor: "pointer"
527
- },
528
- ".cm-panel input.cm-textfield": {},
529
- ".cm-panel input[type=checkbox]": {
530
- width: "8px",
531
- height: "8px",
532
- marginRight: "6px !important",
533
- padding: "2px !important",
534
- color: "var(--dx-accentFocusIndicator)"
535
- },
536
- ".cm-panel button": {
537
- "&:hover": {
538
- backgroundColor: "var(--dx-accentSurfaceHover) !important"
539
- },
540
- "&:active": {
541
- backgroundColor: "var(--dx-accentSurfaceHover)"
542
- }
543
- },
544
- ".cm-panel.cm-search": {
545
- padding: "4px",
546
- borderTop: "1px solid var(--dx-separator)"
547
- }
548
- };
549
-
550
- // packages/ui/react-ui-editor/src/defaults.ts
551
- var margin = "!mt-[1rem]";
552
- var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
553
- var editorContent = mx2(margin, editorWidth);
554
- var editorFullWidth = mx2(margin);
555
- var editorGutter = EditorView.theme({
556
- // Match margin from content.
557
- // Gutter = 2rem + 1rem margin.
558
- ".cm-gutters": {
559
- marginTop: "1rem",
560
- paddingRight: "1rem"
561
- }
562
- });
563
- var editorMonospace = EditorView.theme({
564
- ".cm-content": {
565
- fontFamily: fontMono
566
- }
567
- });
568
- var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
569
- var stackItemContentEditorClassNames = (role) => mx2("attention-surface dx-focus-ring-inset data-[toolbar=disabled]:pbs-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24" : "min-bs-0");
570
- var stackItemContentToolbarClassNames = (role) => mx2("attention-surface is-full border-be !border-separator relative z-[1]", role === "section" && "sticky block-start-0 -mbe-px min-is-0");
571
-
572
- // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
573
- var createToolbar = ({ state, customActions, ...features }) => {
574
- const nodes = [];
575
- const edges = [];
576
- if (features.headings ?? true) {
577
- const headings2 = createHeadings(state);
578
- nodes.push(...headings2.nodes);
579
- edges.push(...headings2.edges);
580
- }
581
- if (features.formatting ?? true) {
582
- const formatting = createFormatting(state);
583
- nodes.push(...formatting.nodes);
584
- edges.push(...formatting.edges);
585
- }
586
- if (features.lists ?? true) {
587
- const lists = createLists(state);
588
- nodes.push(...lists.nodes);
589
- edges.push(...lists.edges);
590
- }
591
- if (features.blocks ?? true) {
592
- const blocks = createBlocks(state);
593
- nodes.push(...blocks.nodes);
594
- edges.push(...blocks.edges);
595
- }
596
- if (customActions) {
597
- const custom = customActions();
598
- nodes.push(...custom.nodes);
599
- edges.push(...custom.edges);
600
- }
601
- const editorToolbarGap = createGapSeparator();
602
- nodes.push(...editorToolbarGap.nodes);
603
- edges.push(...editorToolbarGap.edges);
604
- if (features.comment ?? true) {
605
- const comment = createComment(state);
606
- nodes.push(...comment.nodes);
607
- edges.push(...comment.edges);
608
- }
609
- if (features.search ?? true) {
610
- nodes.push(editorToolbarSearch);
611
- edges.push({
612
- source: "root",
613
- target: editorToolbarSearch.id
614
- });
615
- }
616
- if (features.viewMode ?? true) {
617
- const viewMode = createViewMode(state);
618
- nodes.push(...viewMode.nodes);
619
- edges.push(...viewMode.edges);
620
- }
621
- return {
622
- nodes,
623
- edges
624
- };
625
- };
626
- var useEditorToolbarActionGraph = ({ onAction, ...props }) => {
627
- const menuCreator = useCallback(() => createToolbar(props), [
628
- props
629
- ]);
630
- const { resolveGroupItems } = useMenuActions(menuCreator);
631
- return {
632
- resolveGroupItems,
633
- onAction
634
- };
635
- };
636
- var EditorToolbar = ({ classNames, attendableId, role, ...props }) => {
637
- const menuProps = useEditorToolbarActionGraph(props);
638
- return /* @__PURE__ */ React.createElement("div", {
639
- role: "none",
640
- className: stackItemContentToolbarClassNames(role)
641
- }, /* @__PURE__ */ React.createElement(ElevationProvider, {
642
- elevation: role === "section" ? "positioned" : "base"
643
- }, /* @__PURE__ */ React.createElement(MenuProvider, {
644
- ...menuProps,
645
- attendableId
646
- }, /* @__PURE__ */ React.createElement(ToolbarMenu, {
647
- classNames: [
648
- textBlockWidth,
649
- "!bg-transparent",
650
- classNames
651
- ]
652
- }))));
653
- };
654
-
655
- // packages/ui/react-ui-editor/src/extensions/annotations.ts
656
- import { StateField } from "@codemirror/state";
657
- import { Decoration, EditorView as EditorView2 } from "@codemirror/view";
658
- import { isNotFalsy } from "@dxos/util";
659
-
660
- // packages/ui/react-ui-editor/src/util/facet.ts
661
- import { Facet } from "@codemirror/state";
662
- var singleValueFacet = (defaultValue) => Facet.define({
663
- // Called immediately.
664
- combine: (providers) => {
665
- return providers[0] ?? defaultValue;
666
- }
667
- });
668
-
669
- // packages/ui/react-ui-editor/src/util/cursor.ts
670
- var overlap = (a, b) => a.from <= b.to && a.to >= b.from;
671
- var defaultCursorConverter = {
672
- toCursor: (position) => position.toString(),
673
- fromCursor: (cursor) => parseInt(cursor)
674
- };
675
- var Cursor = class _Cursor {
676
- static {
677
- this.converter = singleValueFacet(defaultCursorConverter);
678
- }
679
- static {
680
- this.getCursorFromRange = (state, range) => {
681
- const cursorConverter2 = state.facet(_Cursor.converter);
682
- const from = cursorConverter2.toCursor(range.from);
683
- const to = cursorConverter2.toCursor(range.to, -1);
684
- return [
685
- from,
686
- to
687
- ].join(":");
688
- };
689
- }
690
- static {
691
- this.getRangeFromCursor = (state, cursor) => {
692
- const cursorConverter2 = state.facet(_Cursor.converter);
693
- const parts = cursor.split(":");
694
- const from = cursorConverter2.fromCursor(parts[0]);
695
- const to = cursorConverter2.fromCursor(parts[1]);
696
- return from !== void 0 && to !== void 0 ? {
697
- from,
698
- to
699
- } : void 0;
700
- };
701
- }
702
- };
703
-
704
- // packages/ui/react-ui-editor/src/util/debug.ts
705
- import { log } from "@dxos/log";
706
- var __dxlog_file = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/util/debug.ts";
707
- var wrapWithCatch = (fn) => {
708
- return (...args) => {
709
- try {
710
- return fn(...args);
711
- } catch (err) {
712
- log.catch(err, void 0, {
713
- F: __dxlog_file,
714
- L: 15,
715
- S: void 0,
716
- C: (f, a) => f(...a)
717
- });
718
- }
719
- };
720
- };
721
- var callbackWrapper = (fn) => (...args) => {
722
- try {
723
- return fn(...args);
724
- } catch (err) {
725
- log.catch(err, void 0, {
726
- F: __dxlog_file,
727
- L: 29,
728
- S: void 0,
729
- C: (f, a) => f(...a)
730
- });
731
- }
732
- };
733
- var debugDispatcher = (trs, view) => {
734
- logChanges(trs);
735
- view.update(trs);
736
- };
737
- var logChanges = (trs) => {
738
- const changes = trs.flatMap((tr) => {
739
- if (tr.changes.empty) {
740
- return void 0;
155
+ var logChanges = (trs) => {
156
+ const changes = trs.flatMap((tr) => {
157
+ if (tr.changes.empty) {
158
+ return void 0;
741
159
  }
742
160
  const changes2 = [];
743
161
  tr.changes.iterChanges((fromA, toA, fromB, toB, inserted) => changes2.push(JSON.stringify({
@@ -789,7 +207,7 @@ var clientRectsFor = (dom) => {
789
207
  };
790
208
 
791
209
  // packages/ui/react-ui-editor/src/util/react.tsx
792
- import React2 from "react";
210
+ import React from "react";
793
211
  import { createRoot } from "react-dom/client";
794
212
  import { ThemeProvider, Tooltip } from "@dxos/react-ui";
795
213
  import { defaultTx } from "@dxos/react-ui-theme";
@@ -806,15 +224,15 @@ var createElement = (tag, options, children) => {
806
224
  return el;
807
225
  };
808
226
  var renderRoot = (root, node) => {
809
- createRoot(root).render(/* @__PURE__ */ React2.createElement(ThemeProvider, {
227
+ createRoot(root).render(/* @__PURE__ */ React.createElement(ThemeProvider, {
810
228
  tx: defaultTx
811
229
  }, node));
812
230
  return root;
813
231
  };
814
232
  var createRenderer = (Component) => (el, props) => {
815
- renderRoot(el, /* @__PURE__ */ React2.createElement(ThemeProvider, {
233
+ renderRoot(el, /* @__PURE__ */ React.createElement(ThemeProvider, {
816
234
  tx: defaultTx
817
- }, /* @__PURE__ */ React2.createElement(Tooltip.Provider, null, /* @__PURE__ */ React2.createElement(Component, props))));
235
+ }, /* @__PURE__ */ React.createElement(Tooltip.Provider, null, /* @__PURE__ */ React.createElement(Component, props))));
818
236
  };
819
237
 
820
238
  // packages/ui/react-ui-editor/src/extensions/annotations.ts
@@ -854,7 +272,7 @@ var annotations = (options = {}) => {
854
272
  });
855
273
  return [
856
274
  annotationsState,
857
- EditorView2.decorations.compute([
275
+ EditorView.decorations.compute([
858
276
  annotationsState
859
277
  ], (state) => {
860
278
  const annotations2 = state.field(annotationsState);
@@ -867,7 +285,7 @@ var annotations = (options = {}) => {
867
285
  styles
868
286
  ];
869
287
  };
870
- var styles = EditorView2.theme({
288
+ var styles = EditorView.theme({
871
289
  ".cm-annotation": {
872
290
  textDecoration: "underline",
873
291
  textDecorationStyle: "wavy",
@@ -915,9 +333,9 @@ var autocomplete = ({ debug, activateOnTyping, override, onSearch } = {}) => {
915
333
  };
916
334
 
917
335
  // packages/ui/react-ui-editor/src/extensions/automerge/automerge.ts
336
+ import { next as A3 } from "@automerge/automerge";
918
337
  import { StateField as StateField2 } from "@codemirror/state";
919
- import { EditorView as EditorView3, ViewPlugin } from "@codemirror/view";
920
- import { next as A3 } from "@dxos/automerge/automerge";
338
+ import { EditorView as EditorView2, ViewPlugin } from "@codemirror/view";
921
339
 
922
340
  // packages/ui/react-ui-editor/src/extensions/automerge/cursor.ts
923
341
  import { log as log2 } from "@dxos/log";
@@ -966,10 +384,10 @@ var isReconcile = (tr) => {
966
384
  };
967
385
 
968
386
  // packages/ui/react-ui-editor/src/extensions/automerge/sync.ts
969
- import { next as A2 } from "@dxos/automerge/automerge";
387
+ import { next as A2 } from "@automerge/automerge";
970
388
 
971
389
  // packages/ui/react-ui-editor/src/extensions/automerge/update-automerge.ts
972
- import { next as A } from "@dxos/automerge/automerge";
390
+ import { next as A } from "@automerge/automerge";
973
391
  var updateAutomerge = (field, handle, transactions, state) => {
974
392
  const { lastHeads, path } = state.field(field);
975
393
  let hasChanges = false;
@@ -1138,8 +556,8 @@ var Syncer = class {
1138
556
  }
1139
557
  onAutomergeChange(view) {
1140
558
  const oldHeads = getLastHeads(view.state, this._state);
1141
- const newHeads = A2.getHeads(this._handle.docSync());
1142
- const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.docSync(), oldHeads, newHeads);
559
+ const newHeads = A2.getHeads(this._handle.doc());
560
+ const diff = A2.equals(oldHeads, newHeads) ? [] : A2.diff(this._handle.doc(), oldHeads, newHeads);
1143
561
  const selection = view.state.selection;
1144
562
  const path = getPath(view.state, this._state);
1145
563
  updateCodeMirror(view, selection, path, diff);
@@ -1155,7 +573,7 @@ var automerge = (accessor) => {
1155
573
  const syncState = StateField2.define({
1156
574
  create: () => ({
1157
575
  path: accessor.path.slice(),
1158
- lastHeads: A3.getHeads(accessor.handle.docSync()),
576
+ lastHeads: A3.getHeads(accessor.handle.doc()),
1159
577
  unreconciledTransactions: []
1160
578
  }),
1161
579
  update: (value, tr) => {
@@ -1200,7 +618,7 @@ var automerge = (accessor) => {
1200
618
  }
1201
619
  }),
1202
620
  // Reconcile local updates.
1203
- EditorView3.updateListener.of(({ view, changes }) => {
621
+ EditorView2.updateListener.of(({ view, changes }) => {
1204
622
  if (!changes.empty) {
1205
623
  syncer.reconcile(view, true);
1206
624
  }
@@ -1210,7 +628,7 @@ var automerge = (accessor) => {
1210
628
 
1211
629
  // packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts
1212
630
  import { Annotation as Annotation2, RangeSet } from "@codemirror/state";
1213
- import { Decoration as Decoration2, EditorView as EditorView4, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
631
+ import { Decoration as Decoration2, EditorView as EditorView3, ViewPlugin as ViewPlugin2, WidgetType } from "@codemirror/view";
1214
632
  import { Event } from "@dxos/async";
1215
633
  import { Context } from "@dxos/context";
1216
634
  var __dxlog_file3 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/awareness/awareness.ts";
@@ -1384,7 +802,7 @@ var RemoteCaretWidget = class extends WidgetType {
1384
802
  return true;
1385
803
  }
1386
804
  };
1387
- var styles2 = EditorView4.theme({
805
+ var styles2 = EditorView3.theme({
1388
806
  ".cm-collab-selection": {},
1389
807
  ".cm-collab-selectionLine": {
1390
808
  padding: 0,
@@ -1550,7 +968,7 @@ var SpaceAwarenessProvider = class {
1550
968
  };
1551
969
 
1552
970
  // packages/ui/react-ui-editor/src/extensions/blast.ts
1553
- import { EditorView as EditorView5, keymap as keymap2 } from "@codemirror/view";
971
+ import { EditorView as EditorView4, keymap as keymap2 } from "@codemirror/view";
1554
972
  import defaultsDeep from "lodash.defaultsdeep";
1555
973
  import { invariant as invariant2 } from "@dxos/invariant";
1556
974
  var __dxlog_file5 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/blast.ts";
@@ -1598,7 +1016,7 @@ var blast = (options = defaultOptions) => {
1598
1016
  };
1599
1017
  return [
1600
1018
  // Cursor moved.
1601
- EditorView5.updateListener.of((update2) => {
1019
+ EditorView4.updateListener.of((update2) => {
1602
1020
  if (blaster?.node !== update2.view.scrollDOM) {
1603
1021
  if (blaster) {
1604
1022
  blaster.destroy();
@@ -1972,11 +1390,11 @@ var commandKeyBindings = [
1972
1390
  ];
1973
1391
 
1974
1392
  // packages/ui/react-ui-editor/src/extensions/command/command.ts
1975
- import { EditorView as EditorView7, keymap as keymap3 } from "@codemirror/view";
1393
+ import { EditorView as EditorView6, keymap as keymap3 } from "@codemirror/view";
1976
1394
 
1977
1395
  // packages/ui/react-ui-editor/src/extensions/command/hint.ts
1978
1396
  import { RangeSetBuilder } from "@codemirror/state";
1979
- import { Decoration as Decoration3, EditorView as EditorView6, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1397
+ import { Decoration as Decoration3, EditorView as EditorView5, ViewPlugin as ViewPlugin3, WidgetType as WidgetType2 } from "@codemirror/view";
1980
1398
  var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
1981
1399
  constructor() {
1982
1400
  this.deco = Decoration3.none;
@@ -2000,7 +1418,7 @@ var hintViewPlugin = ({ onHint }) => ViewPlugin3.fromClass(class {
2000
1418
  }
2001
1419
  }, {
2002
1420
  provide: (plugin) => [
2003
- EditorView6.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
1421
+ EditorView5.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration3.none)
2004
1422
  ]
2005
1423
  });
2006
1424
  var CommandHint = class extends WidgetType2 {
@@ -2119,10 +1537,10 @@ var command = (options = {}) => {
2119
1537
  options.onHint ? hintViewPlugin({
2120
1538
  onHint: options.onHint
2121
1539
  }) : [],
2122
- EditorView7.focusChangeEffect.of((_, focusing) => {
1540
+ EditorView6.focusChangeEffect.of((_, focusing) => {
2123
1541
  return focusing ? closeEffect.of(null) : null;
2124
1542
  }),
2125
- EditorView7.theme({
1543
+ EditorView6.theme({
2126
1544
  ".cm-tooltip": {
2127
1545
  background: "transparent"
2128
1546
  }
@@ -2133,7 +1551,7 @@ var command = (options = {}) => {
2133
1551
  // packages/ui/react-ui-editor/src/extensions/comments.ts
2134
1552
  import { invertedEffects } from "@codemirror/commands";
2135
1553
  import { StateEffect as StateEffect3, StateField as StateField4 } from "@codemirror/state";
2136
- import { hoverTooltip, keymap as keymap5, Decoration as Decoration4, EditorView as EditorView9, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
1554
+ import { hoverTooltip, keymap as keymap5, Decoration as Decoration4, EditorView as EditorView8, ViewPlugin as ViewPlugin5 } from "@codemirror/view";
2137
1555
  import sortBy from "lodash.sortby";
2138
1556
  import { useEffect, useMemo as useMemo2 } from "react";
2139
1557
  import { debounce as debounce2 } from "@dxos/async";
@@ -2142,7 +1560,7 @@ import { isNonNullable } from "@dxos/util";
2142
1560
 
2143
1561
  // packages/ui/react-ui-editor/src/extensions/selection.ts
2144
1562
  import { Transaction } from "@codemirror/state";
2145
- import { EditorView as EditorView8, keymap as keymap4 } from "@codemirror/view";
1563
+ import { EditorView as EditorView7, keymap as keymap4 } from "@codemirror/view";
2146
1564
  import { debounce } from "@dxos/async";
2147
1565
  import { invariant as invariant3 } from "@dxos/invariant";
2148
1566
  import { isNotFalsy as isNotFalsy2 } from "@dxos/util";
@@ -2153,7 +1571,7 @@ var createEditorStateTransaction = ({ scrollTo, selection }) => {
2153
1571
  return {
2154
1572
  selection,
2155
1573
  scrollIntoView: !scrollTo,
2156
- effects: scrollTo ? EditorView8.scrollIntoView(scrollTo, {
1574
+ effects: scrollTo ? EditorView7.scrollIntoView(scrollTo, {
2157
1575
  yMargin: 96
2158
1576
  }) : void 0,
2159
1577
  annotations: Transaction.userEvent.of(stateRestoreAnnotation)
@@ -2195,7 +1613,7 @@ var selectionState = ({ getState, setState } = {}) => {
2195
1613
  // setStateDebounced(id, {});
2196
1614
  // },
2197
1615
  // }),
2198
- EditorView8.updateListener.of(({ view, transactions }) => {
1616
+ EditorView7.updateListener.of(({ view, transactions }) => {
2199
1617
  const id = view.state.facet(documentId);
2200
1618
  if (!id || transactions.some((tr) => tr.isUserEvent(stateRestoreAnnotation))) {
2201
1619
  return;
@@ -2276,7 +1694,7 @@ var commentsState = StateField4.define({
2276
1694
  return value;
2277
1695
  }
2278
1696
  });
2279
- var styles3 = EditorView9.theme({
1697
+ var styles3 = EditorView8.theme({
2280
1698
  ".cm-comment, .cm-comment-current": {
2281
1699
  margin: "0 -3px",
2282
1700
  padding: "3px",
@@ -2296,7 +1714,7 @@ var createCommentMark = (id, isCurrent) => Decoration4.mark({
2296
1714
  "data-comment-id": id
2297
1715
  }
2298
1716
  });
2299
- var commentsDecorations = EditorView9.decorations.compute([
1717
+ var commentsDecorations = EditorView8.decorations.compute([
2300
1718
  commentsState
2301
1719
  ], (state) => {
2302
1720
  const { selection: { current }, comments: comments2 } = state.field(commentsState);
@@ -2319,7 +1737,7 @@ var commentsDecorations = EditorView9.decorations.compute([
2319
1737
  return Decoration4.set(decorations);
2320
1738
  });
2321
1739
  var commentClickedEffect = StateEffect3.define();
2322
- var handleCommentClick = EditorView9.domEventHandlers({
1740
+ var handleCommentClick = EditorView8.domEventHandlers({
2323
1741
  click: (event, view) => {
2324
1742
  let target = event.target;
2325
1743
  const editorRoot = view.dom;
@@ -2358,7 +1776,7 @@ var trackPastedComments = (onUpdate) => {
2358
1776
  }
2359
1777
  };
2360
1778
  return [
2361
- EditorView9.domEventHandlers({
1779
+ EditorView8.domEventHandlers({
2362
1780
  cut: handleTrack,
2363
1781
  copy: handleTrack
2364
1782
  }),
@@ -2380,7 +1798,7 @@ var trackPastedComments = (onUpdate) => {
2380
1798
  return effects;
2381
1799
  }),
2382
1800
  // Handle paste or the undo of comment deletion.
2383
- EditorView9.updateListener.of((update2) => {
1801
+ EditorView8.updateListener.of((update2) => {
2384
1802
  const restore = [];
2385
1803
  for (let i = 0; i < update2.transactions.length; i++) {
2386
1804
  const tr = update2.transactions[i];
@@ -2439,7 +1857,7 @@ var mapTrackedComment = (comment, changes) => ({
2439
1857
  var restoreCommentEffect = StateEffect3.define({
2440
1858
  map: mapTrackedComment
2441
1859
  });
2442
- var createComment2 = (view) => {
1860
+ var createComment = (view) => {
2443
1861
  const options = view.state.facet(optionsFacet);
2444
1862
  const { from, to } = view.state.selection.main;
2445
1863
  if (from === to) {
@@ -2484,7 +1902,7 @@ var comments = (options = {}) => {
2484
1902
  options.onCreate && keymap5.of([
2485
1903
  {
2486
1904
  key: shortcut,
2487
- run: callbackWrapper(createComment2)
1905
+ run: callbackWrapper(createComment)
2488
1906
  }
2489
1907
  ]),
2490
1908
  //
@@ -2522,7 +1940,7 @@ var comments = (options = {}) => {
2522
1940
  //
2523
1941
  // Track deleted ranges and update ranges for decorations.
2524
1942
  //
2525
- EditorView9.updateListener.of(({ view, state, changes }) => {
1943
+ EditorView8.updateListener.of(({ view, state, changes }) => {
2526
1944
  let mod = false;
2527
1945
  const { comments: comments2, ...value } = state.field(commentsState);
2528
1946
  changes.iterChanges((from, to, from2, to2) => {
@@ -2554,7 +1972,7 @@ var comments = (options = {}) => {
2554
1972
  //
2555
1973
  // Track selection/proximity.
2556
1974
  //
2557
- EditorView9.updateListener.of(({ view, state }) => {
1975
+ EditorView8.updateListener.of(({ view, state }) => {
2558
1976
  let min = Infinity;
2559
1977
  const { selection: { current, closest }, comments: comments2 } = state.field(commentsState);
2560
1978
  const { head } = state.selection.main;
@@ -2608,7 +2026,7 @@ var scrollThreadIntoView = (view, id, center = true) => {
2608
2026
  anchor: range.from
2609
2027
  } : void 0,
2610
2028
  effects: [
2611
- needsScroll ? EditorView9.scrollIntoView(range.from, center ? {
2029
+ needsScroll ? EditorView8.scrollIntoView(range.from, center ? {
2612
2030
  y: "center"
2613
2031
  } : void 0) : [],
2614
2032
  needsSelectionUpdate ? setSelection.of({
@@ -2660,7 +2078,7 @@ var createExternalCommentSync = (id, subscribe, getComments) => ViewPlugin5.from
2660
2078
  }
2661
2079
  });
2662
2080
  var useCommentState = (state) => {
2663
- return useMemo2(() => EditorView9.updateListener.of((update2) => {
2081
+ return useMemo2(() => EditorView8.updateListener.of((update2) => {
2664
2082
  if (update2.docChanged || update2.selectionSet) {
2665
2083
  state.comment = selectionOverlapsComment(update2.state);
2666
2084
  state.selection = hasActiveSelection(update2.state);
@@ -2684,7 +2102,7 @@ var useComments = (view, id, comments2) => {
2684
2102
  });
2685
2103
  };
2686
2104
  var useCommentClickListener = (onCommentClick) => {
2687
- return useMemo2(() => EditorView9.updateListener.of((update2) => {
2105
+ return useMemo2(() => EditorView8.updateListener.of((update2) => {
2688
2106
  update2.transactions.forEach((transaction) => {
2689
2107
  transaction.effects.forEach((effect) => {
2690
2108
  if (effect.is(commentClickedEffect)) {
@@ -2700,9 +2118,9 @@ var useCommentClickListener = (onCommentClick) => {
2700
2118
  // packages/ui/react-ui-editor/src/extensions/debug.ts
2701
2119
  import { syntaxTree } from "@codemirror/language";
2702
2120
  import { StateField as StateField5 } from "@codemirror/state";
2703
- var debugNodeLogger = (log8 = console.log) => {
2121
+ var debugNodeLogger = (log9 = console.log) => {
2704
2122
  const logTokens = (state) => syntaxTree(state).iterate({
2705
- enter: (node) => log8(node.type)
2123
+ enter: (node) => log9(node.type)
2706
2124
  });
2707
2125
  return StateField5.define({
2708
2126
  create: (state) => logTokens(state),
@@ -2711,8 +2129,8 @@ var debugNodeLogger = (log8 = console.log) => {
2711
2129
  };
2712
2130
 
2713
2131
  // packages/ui/react-ui-editor/src/extensions/dnd.ts
2714
- import { dropCursor, EditorView as EditorView10 } from "@codemirror/view";
2715
- var styles4 = EditorView10.theme({
2132
+ import { dropCursor, EditorView as EditorView9 } from "@codemirror/view";
2133
+ var styles4 = EditorView9.theme({
2716
2134
  ".cm-dropCursor": {
2717
2135
  borderLeft: "2px solid var(--dx-accentText)",
2718
2136
  color: "var(--dx-accentText)",
@@ -2726,7 +2144,7 @@ var dropFile = (options = {}) => {
2726
2144
  return [
2727
2145
  styles4,
2728
2146
  dropCursor(),
2729
- EditorView10.domEventHandlers({
2147
+ EditorView9.domEventHandlers({
2730
2148
  drop: (event, view) => {
2731
2149
  event.preventDefault();
2732
2150
  const files = event.dataTransfer?.files;
@@ -2753,7 +2171,7 @@ import { bracketMatching, defaultHighlightStyle, syntaxHighlighting } from "@cod
2753
2171
  import { searchKeymap } from "@codemirror/search";
2754
2172
  import { EditorState } from "@codemirror/state";
2755
2173
  import { oneDarkHighlightStyle } from "@codemirror/theme-one-dark";
2756
- import { EditorView as EditorView12, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap6, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2174
+ import { EditorView as EditorView11, drawSelection, dropCursor as dropCursor2, highlightActiveLine, keymap as keymap6, lineNumbers, placeholder, scrollPastEnd } from "@codemirror/view";
2757
2175
  import defaultsDeep2 from "lodash.defaultsdeep";
2758
2176
  import merge from "lodash.merge";
2759
2177
  import { generateName } from "@dxos/display-name";
@@ -2762,7 +2180,7 @@ import { hexToHue, isNotFalsy as isNotFalsy3 } from "@dxos/util";
2762
2180
 
2763
2181
  // packages/ui/react-ui-editor/src/extensions/focus.ts
2764
2182
  import { StateEffect as StateEffect4, StateField as StateField6 } from "@codemirror/state";
2765
- import { EditorView as EditorView11 } from "@codemirror/view";
2183
+ import { EditorView as EditorView10 } from "@codemirror/view";
2766
2184
  var focusEffect = StateEffect4.define();
2767
2185
  var focusField = StateField6.define({
2768
2186
  create: () => false,
@@ -2777,19 +2195,254 @@ var focusField = StateField6.define({
2777
2195
  });
2778
2196
  var focus = [
2779
2197
  focusField,
2780
- EditorView11.domEventHandlers({
2198
+ EditorView10.domEventHandlers({
2781
2199
  focus: (event, view) => {
2782
2200
  setTimeout(() => view.dispatch({
2783
2201
  effects: focusEffect.of(true)
2784
2202
  }));
2785
2203
  },
2786
- blur: (event, view) => {
2787
- setTimeout(() => view.dispatch({
2788
- effects: focusEffect.of(false)
2789
- }));
2204
+ blur: (event, view) => {
2205
+ setTimeout(() => view.dispatch({
2206
+ effects: focusEffect.of(false)
2207
+ }));
2208
+ }
2209
+ })
2210
+ ];
2211
+
2212
+ // packages/ui/react-ui-editor/src/styles/markdown.ts
2213
+ import { mx } from "@dxos/react-ui-theme";
2214
+ var headings = {
2215
+ 1: "text-4xl",
2216
+ 2: "text-3xl",
2217
+ 3: "text-2xl",
2218
+ 4: "text-xl",
2219
+ 5: "text-lg",
2220
+ 6: ""
2221
+ };
2222
+ var theme = {
2223
+ code: "font-mono !no-underline text-neutral-700 dark:text-neutral-300",
2224
+ codeMark: "font-mono text-primary-500",
2225
+ mark: "opacity-50",
2226
+ heading: (level) => {
2227
+ return mx(headings[level], "dark:text-primary-400");
2228
+ }
2229
+ };
2230
+
2231
+ // packages/ui/react-ui-editor/src/styles/tokens.ts
2232
+ import get from "lodash.get";
2233
+ import { tokens } from "@dxos/react-ui-theme";
2234
+ var getToken = (path, defaultValue) => {
2235
+ const value = get(tokens, path, defaultValue);
2236
+ return value?.toString() ?? "";
2237
+ };
2238
+ var fontBody = getToken("fontFamily.body");
2239
+ var fontMono = getToken("fontFamily.mono");
2240
+
2241
+ // packages/ui/react-ui-editor/src/styles/theme.ts
2242
+ var defaultTheme = {
2243
+ "&": {},
2244
+ "&.cm-focused": {
2245
+ outline: "none"
2246
+ },
2247
+ /**
2248
+ * Scroller
2249
+ */
2250
+ ".cm-scroller": {
2251
+ overflowY: "auto"
2252
+ },
2253
+ /**
2254
+ * Content
2255
+ * NOTE: Apply margins to content so that scrollbar is at the edge of the container.
2256
+ */
2257
+ ".cm-content": {
2258
+ padding: "unset",
2259
+ fontFamily: fontBody,
2260
+ // NOTE: Base font size (otherwise defined by HTML tag, which might be different for storybook).
2261
+ fontSize: "16px",
2262
+ lineHeight: 1.5,
2263
+ color: "unset"
2264
+ },
2265
+ /**
2266
+ * Gutters
2267
+ * NOTE: Gutters should have the same top margin as the content.
2268
+ */
2269
+ ".cm-gutters": {
2270
+ borderRight: "none",
2271
+ background: "transparent"
2272
+ },
2273
+ ".cm-gutter": {},
2274
+ ".cm-gutter.cm-lineNumbers": {
2275
+ paddingRight: "4px",
2276
+ borderRight: "1px solid var(--dx-separator)"
2277
+ },
2278
+ ".cm-gutter.cm-lineNumbers .cm-gutterElement": {
2279
+ minWidth: "40px",
2280
+ alignContent: "center"
2281
+ },
2282
+ /**
2283
+ * Height is set to match the corresponding line.
2284
+ */
2285
+ ".cm-gutterElement": {
2286
+ alignItems: "center",
2287
+ fontSize: "12px"
2288
+ },
2289
+ /**
2290
+ * Line.
2291
+ */
2292
+ ".cm-line": {
2293
+ paddingInline: 0
2294
+ },
2295
+ ".cm-activeLine": {
2296
+ background: "var(--dx-cmActiveLine)"
2297
+ },
2298
+ /**
2299
+ * Cursor (layer).
2300
+ */
2301
+ ".cm-cursor, .cm-dropCursor": {
2302
+ borderLeft: "2px solid var(--dx-cmCursor)"
2303
+ },
2304
+ ".cm-placeholder": {
2305
+ color: "var(--dx-subdued)"
2306
+ },
2307
+ /**
2308
+ * Selection (layer).
2309
+ */
2310
+ ".cm-selectionBackground": {
2311
+ background: "var(--dx-cmSelection)"
2312
+ },
2313
+ "&.cm-focused > .cm-scroller > .cm-selectionLayer .cm-selectionBackground": {
2314
+ background: "var(--dx-cmFocusedSelection)"
2315
+ },
2316
+ /**
2317
+ * Search.
2318
+ * NOTE: Matches comment.
2319
+ */
2320
+ ".cm-searchMatch": {
2321
+ margin: "0 -3px",
2322
+ padding: "3px",
2323
+ borderRadius: "3px",
2324
+ background: "var(--dx-cmHighlightSurface)",
2325
+ color: "var(--dx-cmHighlight)"
2326
+ },
2327
+ ".cm-searchMatch-selected": {
2328
+ textDecoration: "underline"
2329
+ },
2330
+ /**
2331
+ * Link.
2332
+ */
2333
+ ".cm-link": {
2334
+ textDecorationLine: "underline",
2335
+ textDecorationThickness: "1px",
2336
+ textDecorationColor: "var(--dx-separator)",
2337
+ textUnderlineOffset: "2px",
2338
+ borderRadius: ".125rem"
2339
+ },
2340
+ ".cm-link > span": {
2341
+ color: "var(--dx-accentText)"
2342
+ },
2343
+ /**
2344
+ * Tooltip.
2345
+ */
2346
+ ".cm-tooltip": {
2347
+ background: "var(--dx-baseSurface)"
2348
+ },
2349
+ ".cm-tooltip-below": {},
2350
+ /**
2351
+ * Autocomplete.
2352
+ * https://github.com/codemirror/autocomplete/blob/main/src/completion.ts
2353
+ */
2354
+ ".cm-tooltip.cm-tooltip-autocomplete": {
2355
+ marginTop: "4px",
2356
+ marginLeft: "-3px"
2357
+ },
2358
+ ".cm-tooltip.cm-tooltip-autocomplete > ul": {
2359
+ maxHeight: "20em"
2360
+ },
2361
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li": {},
2362
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > li[aria-selected]": {},
2363
+ ".cm-tooltip.cm-tooltip-autocomplete > ul > completion-section": {
2364
+ paddingLeft: "4px !important",
2365
+ borderBottom: "none !important",
2366
+ color: "var(--dx-accentText)"
2367
+ },
2368
+ ".cm-tooltip.cm-completionInfo": {
2369
+ width: "360px !important",
2370
+ margin: "-10px 1px 0 1px",
2371
+ padding: "8px !important",
2372
+ borderColor: "var(--dx-separator)"
2373
+ },
2374
+ ".cm-completionIcon": {
2375
+ display: "none"
2376
+ },
2377
+ ".cm-completionLabel": {
2378
+ fontFamily: fontBody
2379
+ },
2380
+ ".cm-completionMatchedText": {
2381
+ textDecoration: "none !important",
2382
+ opacity: 0.5
2383
+ },
2384
+ /**
2385
+ * Panels
2386
+ * https://github.com/codemirror/search/blob/main/src/search.ts#L745
2387
+ *
2388
+ * Find/replace panel.
2389
+ * <div class="cm-announced">...</div>
2390
+ * <div class="cm-scroller">...</div>
2391
+ * <div class="cm-panels cm-panels-bottom">
2392
+ * <div class="cm-search cm-panel">
2393
+ * <input class="cm-textfield" />
2394
+ * <button class="cm-button">...</button>
2395
+ * <label><input type="checkbox" />...</label>
2396
+ * </div>
2397
+ * </div
2398
+ */
2399
+ // TODO(burdon): Implement custom panel (with icon buttons).
2400
+ ".cm-panels": {},
2401
+ ".cm-panel": {
2402
+ fontFamily: fontBody,
2403
+ backgroundColor: "var(--surface-bg)"
2404
+ },
2405
+ ".cm-panel input, .cm-panel button, .cm-panel label": {
2406
+ color: "var(--dx-subdued)",
2407
+ fontFamily: fontBody,
2408
+ fontSize: "14px",
2409
+ all: "unset",
2410
+ margin: "3px !important",
2411
+ padding: "2px 6px !important",
2412
+ outline: "1px solid transparent"
2413
+ },
2414
+ ".cm-panel input, .cm-panel button": {
2415
+ backgroundColor: "var(--dx-input)"
2416
+ },
2417
+ ".cm-panel input:focus, .cm-panel button:focus": {
2418
+ outline: "1px solid var(--dx-accentFocusIndicator)"
2419
+ },
2420
+ ".cm-panel label": {
2421
+ display: "inline-flex",
2422
+ alignItems: "center",
2423
+ cursor: "pointer"
2424
+ },
2425
+ ".cm-panel input.cm-textfield": {},
2426
+ ".cm-panel input[type=checkbox]": {
2427
+ width: "8px",
2428
+ height: "8px",
2429
+ marginRight: "6px !important",
2430
+ padding: "2px !important",
2431
+ color: "var(--dx-accentFocusIndicator)"
2432
+ },
2433
+ ".cm-panel button": {
2434
+ "&:hover": {
2435
+ backgroundColor: "var(--dx-accentSurfaceHover) !important"
2436
+ },
2437
+ "&:active": {
2438
+ backgroundColor: "var(--dx-accentSurfaceHover)"
2790
2439
  }
2791
- })
2792
- ];
2440
+ },
2441
+ ".cm-panel.cm-search": {
2442
+ padding: "4px",
2443
+ borderTop: "1px solid var(--dx-separator)"
2444
+ }
2445
+ };
2793
2446
 
2794
2447
  // packages/ui/react-ui-editor/src/extensions/factories.ts
2795
2448
  var __dxlog_file8 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/factories.ts";
@@ -2815,7 +2468,7 @@ var createBasicExtensions = (_props) => {
2815
2468
  const props = defaultsDeep2({}, _props, defaultBasicOptions);
2816
2469
  return [
2817
2470
  // NOTE: Doesn't catch errors in keymap functions.
2818
- EditorView12.exceptionSink.of((err) => {
2471
+ EditorView11.exceptionSink.of((err) => {
2819
2472
  log5.catch(err, void 0, {
2820
2473
  F: __dxlog_file8,
2821
2474
  L: 96,
@@ -2830,12 +2483,12 @@ var createBasicExtensions = (_props) => {
2830
2483
  props.drawSelection && drawSelection({
2831
2484
  cursorBlinkRate: 1200
2832
2485
  }),
2833
- props.editable !== void 0 && EditorView12.editable.of(props.editable),
2486
+ props.editable !== void 0 && EditorView11.editable.of(props.editable),
2834
2487
  props.focus && focus,
2835
2488
  props.highlightActiveLine && highlightActiveLine(),
2836
2489
  props.history && history(),
2837
2490
  props.lineNumbers && lineNumbers(),
2838
- props.lineWrapping && EditorView12.lineWrapping,
2491
+ props.lineWrapping && EditorView11.lineWrapping,
2839
2492
  props.placeholder && placeholder(props.placeholder),
2840
2493
  props.readOnly !== void 0 && EditorState.readOnly.of(props.readOnly),
2841
2494
  props.scrollPastEnd && scrollPastEnd(),
@@ -2872,14 +2525,14 @@ var defaultThemeSlots = {
2872
2525
  var createThemeExtensions = ({ themeMode, styles: styles5, syntaxHighlighting: _syntaxHighlighting, slots: _slots } = {}) => {
2873
2526
  const slots = defaultsDeep2({}, _slots, defaultThemeSlots);
2874
2527
  return [
2875
- EditorView12.darkTheme.of(themeMode === "dark"),
2876
- EditorView12.baseTheme(styles5 ? merge({}, defaultTheme, styles5) : defaultTheme),
2528
+ EditorView11.darkTheme.of(themeMode === "dark"),
2529
+ EditorView11.baseTheme(styles5 ? merge({}, defaultTheme, styles5) : defaultTheme),
2877
2530
  // https://github.com/codemirror/theme-one-dark
2878
2531
  _syntaxHighlighting && (themeMode === "dark" ? syntaxHighlighting(oneDarkHighlightStyle) : syntaxHighlighting(defaultHighlightStyle)),
2879
- slots.editor?.className && EditorView12.editorAttributes.of({
2532
+ slots.editor?.className && EditorView11.editorAttributes.of({
2880
2533
  class: slots.editor.className
2881
2534
  }),
2882
- slots.content?.className && EditorView12.contentAttributes.of({
2535
+ slots.content?.className && EditorView11.contentAttributes.of({
2883
2536
  class: slots.content.className
2884
2537
  })
2885
2538
  ].filter(isNotFalsy3);
@@ -2908,8 +2561,8 @@ var createDataExtensions = ({ id, text, space, identity }) => {
2908
2561
 
2909
2562
  // packages/ui/react-ui-editor/src/extensions/folding.tsx
2910
2563
  import { codeFolding, foldGutter } from "@codemirror/language";
2911
- import { EditorView as EditorView13 } from "@codemirror/view";
2912
- import React3 from "react";
2564
+ import { EditorView as EditorView12 } from "@codemirror/view";
2565
+ import React2 from "react";
2913
2566
  import { Icon } from "@dxos/react-ui";
2914
2567
  var folding = (_props = {}) => [
2915
2568
  codeFolding({
@@ -2922,7 +2575,7 @@ var folding = (_props = {}) => [
2922
2575
  const el = createElement("div", {
2923
2576
  className: "flex h-full items-center"
2924
2577
  });
2925
- return renderRoot(el, /* @__PURE__ */ React3.createElement(Icon, {
2578
+ return renderRoot(el, /* @__PURE__ */ React2.createElement(Icon, {
2926
2579
  icon: "ph--caret-right--bold",
2927
2580
  size: 3,
2928
2581
  classNames: [
@@ -2932,7 +2585,7 @@ var folding = (_props = {}) => [
2932
2585
  }));
2933
2586
  }
2934
2587
  }),
2935
- EditorView13.theme({
2588
+ EditorView12.theme({
2936
2589
  ".cm-foldGutter": {
2937
2590
  opacity: 0.3,
2938
2591
  transition: "opacity 0.3s",
@@ -2945,14 +2598,14 @@ var folding = (_props = {}) => [
2945
2598
  ];
2946
2599
 
2947
2600
  // packages/ui/react-ui-editor/src/extensions/listener.ts
2948
- import { EditorView as EditorView14 } from "@codemirror/view";
2601
+ import { EditorView as EditorView13 } from "@codemirror/view";
2949
2602
  var listener = ({ onFocus, onChange }) => {
2950
2603
  const extensions = [];
2951
- onFocus && extensions.push(EditorView14.focusChangeEffect.of((_, focusing) => {
2604
+ onFocus && extensions.push(EditorView13.focusChangeEffect.of((_, focusing) => {
2952
2605
  onFocus(focusing);
2953
2606
  return null;
2954
2607
  }));
2955
- onChange && extensions.push(EditorView14.updateListener.of((update2) => {
2608
+ onChange && extensions.push(EditorView13.updateListener.of((update2) => {
2956
2609
  onChange(update2.state.doc.toString(), update2.state.facet(documentId));
2957
2610
  }));
2958
2611
  return extensions;
@@ -2962,7 +2615,7 @@ var listener = ({ onFocus, onChange }) => {
2962
2615
  import { snippet } from "@codemirror/autocomplete";
2963
2616
  import { syntaxTree as syntaxTree2 } from "@codemirror/language";
2964
2617
  import { EditorSelection } from "@codemirror/state";
2965
- import { EditorView as EditorView15, keymap as keymap7 } from "@codemirror/view";
2618
+ import { EditorView as EditorView14, keymap as keymap7 } from "@codemirror/view";
2966
2619
  import { useMemo as useMemo3 } from "react";
2967
2620
  var formattingEquals = (a, b) => a.blockType === b.blockType && a.strong === b.strong && a.emphasis === b.emphasis && a.strikethrough === b.strikethrough && a.code === b.code && a.link === b.link && a.listStyle === b.listStyle && a.blockQuote === b.blockQuote;
2968
2621
  var Inline;
@@ -4051,7 +3704,7 @@ var getFormatting = (state) => {
4051
3704
  };
4052
3705
  };
4053
3706
  var useFormattingState = (state) => {
4054
- return useMemo3(() => EditorView15.updateListener.of((update2) => {
3707
+ return useMemo3(() => EditorView14.updateListener.of((update2) => {
4055
3708
  if (update2.docChanged || update2.selectionSet) {
4056
3709
  Object.entries(getFormatting(update2.state)).forEach(([key, active]) => {
4057
3710
  state[key] = active;
@@ -4099,7 +3752,7 @@ var processEditorPayload = (view, { type, data }) => {
4099
3752
  })(view);
4100
3753
  break;
4101
3754
  case "comment":
4102
- createComment2(view);
3755
+ createComment(view);
4103
3756
  break;
4104
3757
  }
4105
3758
  requestAnimationFrame(() => {
@@ -4363,9 +4016,9 @@ var convertTreeToJson = (state) => {
4363
4016
  // packages/ui/react-ui-editor/src/extensions/markdown/decorate.ts
4364
4017
  import { syntaxTree as syntaxTree7 } from "@codemirror/language";
4365
4018
  import { RangeSetBuilder as RangeSetBuilder3, StateEffect as StateEffect5 } from "@codemirror/state";
4366
- import { EditorView as EditorView19, Decoration as Decoration7, WidgetType as WidgetType5, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
4019
+ import { EditorView as EditorView18, Decoration as Decoration7, WidgetType as WidgetType5, ViewPlugin as ViewPlugin7 } from "@codemirror/view";
4367
4020
  import { invariant as invariant4 } from "@dxos/invariant";
4368
- import { mx as mx3 } from "@dxos/react-ui-theme";
4021
+ import { mx as mx2 } from "@dxos/react-ui-theme";
4369
4022
 
4370
4023
  // packages/ui/react-ui-editor/src/extensions/markdown/changes.ts
4371
4024
  import { syntaxTree as syntaxTree4 } from "@codemirror/language";
@@ -4415,11 +4068,11 @@ var adjustChanges = () => {
4415
4068
  if (url) {
4416
4069
  const node = tree.resolveInner(fromA, -1);
4417
4070
  const invalidPositions = /* @__PURE__ */ new Set([
4418
- "Link",
4419
- "LinkMark",
4420
4071
  "Code",
4421
4072
  "CodeText",
4422
4073
  "FencedCode",
4074
+ "Link",
4075
+ "LinkMark",
4423
4076
  "URL"
4424
4077
  ]);
4425
4078
  if (!invalidPositions.has(node?.name)) {
@@ -4514,7 +4167,7 @@ var getValidUrl = (str) => {
4514
4167
  // packages/ui/react-ui-editor/src/extensions/markdown/image.ts
4515
4168
  import { syntaxTree as syntaxTree5 } from "@codemirror/language";
4516
4169
  import { StateField as StateField8 } from "@codemirror/state";
4517
- import { Decoration as Decoration5, EditorView as EditorView16, WidgetType as WidgetType3 } from "@codemirror/view";
4170
+ import { Decoration as Decoration5, EditorView as EditorView15, WidgetType as WidgetType3 } from "@codemirror/view";
4518
4171
  var image = (_options = {}) => {
4519
4172
  return [
4520
4173
  StateField8.define({
@@ -4542,7 +4195,7 @@ var image = (_options = {}) => {
4542
4195
  add: buildDecorations(from, to, tr.state)
4543
4196
  });
4544
4197
  },
4545
- provide: (field) => EditorView16.decorations.from(field)
4198
+ provide: (field) => EditorView15.decorations.from(field)
4546
4199
  })
4547
4200
  ];
4548
4201
  };
@@ -4602,10 +4255,10 @@ var ImageWidget = class extends WidgetType3 {
4602
4255
  };
4603
4256
 
4604
4257
  // packages/ui/react-ui-editor/src/extensions/markdown/styles.ts
4605
- import { EditorView as EditorView17 } from "@codemirror/view";
4258
+ import { EditorView as EditorView16 } from "@codemirror/view";
4606
4259
  var bulletListIndentationWidth = 24;
4607
4260
  var orderedListIndentationWidth = 36;
4608
- var formattingStyles = EditorView17.theme({
4261
+ var formattingStyles = EditorView16.theme({
4609
4262
  /**
4610
4263
  * Horizontal rule.
4611
4264
  */
@@ -4652,11 +4305,11 @@ var formattingStyles = EditorView17.theme({
4652
4305
  background: "var(--dx-cmCodeblock)",
4653
4306
  paddingInline: "1rem !important"
4654
4307
  },
4655
- "& .cm-codeblock-first": {
4308
+ "& .cm-codeblock-start": {
4656
4309
  borderTopLeftRadius: ".25rem",
4657
4310
  borderTopRightRadius: ".25rem"
4658
4311
  },
4659
- "& .cm-codeblock-last": {
4312
+ "& .cm-codeblock-end": {
4660
4313
  borderBottomLeftRadius: ".25rem",
4661
4314
  borderBottomRightRadius: ".25rem"
4662
4315
  },
@@ -4726,12 +4379,12 @@ var formattingStyles = EditorView17.theme({
4726
4379
  // packages/ui/react-ui-editor/src/extensions/markdown/table.ts
4727
4380
  import { syntaxTree as syntaxTree6 } from "@codemirror/language";
4728
4381
  import { RangeSetBuilder as RangeSetBuilder2, StateField as StateField9 } from "@codemirror/state";
4729
- import { Decoration as Decoration6, EditorView as EditorView18, WidgetType as WidgetType4 } from "@codemirror/view";
4382
+ import { Decoration as Decoration6, EditorView as EditorView17, WidgetType as WidgetType4 } from "@codemirror/view";
4730
4383
  var table = (options = {}) => {
4731
4384
  return StateField9.define({
4732
4385
  create: (state) => update(state, options),
4733
4386
  update: (_, tr) => update(tr.state, options),
4734
- provide: (field) => EditorView18.decorations.from(field)
4387
+ provide: (field) => EditorView17.decorations.from(field)
4735
4388
  });
4736
4389
  };
4737
4390
  var update = (state, _options) => {
@@ -4917,16 +4570,16 @@ var TextWidget = class extends WidgetType5 {
4917
4570
  };
4918
4571
  var hide = Decoration7.replace({});
4919
4572
  var blockQuote = Decoration7.line({
4920
- class: mx3("cm-blockquote")
4573
+ class: "cm-blockquote"
4921
4574
  });
4922
4575
  var fencedCodeLine = Decoration7.line({
4923
- class: mx3("cm-code cm-codeblock-line")
4576
+ class: "cm-code cm-codeblock-line"
4924
4577
  });
4925
4578
  var fencedCodeLineFirst = Decoration7.line({
4926
- class: mx3("cm-code cm-codeblock-line", "cm-codeblock-first")
4579
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-start")
4927
4580
  });
4928
4581
  var fencedCodeLineLast = Decoration7.line({
4929
- class: mx3("cm-code cm-codeblock-line", "cm-codeblock-last")
4582
+ class: mx2("cm-code cm-codeblock-line", "cm-codeblock-end")
4930
4583
  });
4931
4584
  var commentBlockLine = fencedCodeLine;
4932
4585
  var commentBlockLineFirst = fencedCodeLineFirst;
@@ -4993,653 +4646,1305 @@ var buildDecorations2 = (view, options, focus2) => {
4993
4646
  number: 0
4994
4647
  });
4995
4648
  };
4996
- const leaveList = () => {
4997
- listLevels.pop();
4998
- };
4999
- const getCurrentListLevel = () => {
5000
- invariant4(listLevels.length, void 0, {
5001
- F: __dxlog_file9,
5002
- L: 201,
5003
- S: void 0,
5004
- A: [
5005
- "listLevels.length",
5006
- ""
4649
+ const leaveList = () => {
4650
+ listLevels.pop();
4651
+ };
4652
+ const getCurrentListLevel = () => {
4653
+ invariant4(listLevels.length, void 0, {
4654
+ F: __dxlog_file9,
4655
+ L: 201,
4656
+ S: void 0,
4657
+ A: [
4658
+ "listLevels.length",
4659
+ ""
4660
+ ]
4661
+ });
4662
+ return listLevels[listLevels.length - 1];
4663
+ };
4664
+ const enterNode = (node) => {
4665
+ switch (node.name) {
4666
+ // ATXHeading > HeaderMark > Paragraph
4667
+ // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be
4668
+ // processed and the numbering will be incorrect.
4669
+ case "ATXHeading1":
4670
+ case "ATXHeading2":
4671
+ case "ATXHeading3":
4672
+ case "ATXHeading4":
4673
+ case "ATXHeading5":
4674
+ case "ATXHeading6": {
4675
+ const level = parseInt(node.name["ATXHeading".length]);
4676
+ const headers = getHeaderLevels(node, level);
4677
+ if (options.numberedHeadings?.from !== void 0) {
4678
+ const header = headers[level - 1];
4679
+ if (header) {
4680
+ header.number++;
4681
+ }
4682
+ }
4683
+ const editing = editingRange(state, node, focus2);
4684
+ if (editing) {
4685
+ break;
4686
+ }
4687
+ const mark = node.node.firstChild;
4688
+ if (mark?.name === "HeaderMark") {
4689
+ const { from, to = 6 } = options.numberedHeadings ?? {};
4690
+ const text = view.state.sliceDoc(node.from, node.to);
4691
+ const len = text.match(/[#\s]+/)[0].length;
4692
+ if (!from || level < from || level > to) {
4693
+ atomicDeco.add(mark.from, mark.from + len, hide);
4694
+ } else {
4695
+ const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
4696
+ if (num.length) {
4697
+ atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
4698
+ widget: new TextWidget(num, theme.heading(level))
4699
+ }));
4700
+ }
4701
+ }
4702
+ }
4703
+ return false;
4704
+ }
4705
+ //
4706
+ // Lists.
4707
+ // [BulletList | OrderedList] > (ListItem > ListMark) > (Task > TaskMarker)?
4708
+ //
4709
+ case "BulletList":
4710
+ case "OrderedList": {
4711
+ enterList(node);
4712
+ break;
4713
+ }
4714
+ case "ListItem": {
4715
+ const line = state.doc.lineAt(node.from);
4716
+ const list = getCurrentListLevel();
4717
+ const width = list.type === "OrderedList" ? orderedListIndentationWidth : bulletListIndentationWidth;
4718
+ const offset = (options?.listPaddingLeft ?? 0) + ((list.level ?? 0) + 1) * width;
4719
+ if (node.from === line.to - 1) {
4720
+ return false;
4721
+ }
4722
+ deco.add(line.from, line.from, Decoration7.line({
4723
+ class: "cm-list-item",
4724
+ attributes: {
4725
+ style: `padding-left: ${offset}px; text-indent: -${width}px;`
4726
+ }
4727
+ }));
4728
+ break;
4729
+ }
4730
+ case "ListMark": {
4731
+ const list = getCurrentListLevel();
4732
+ const next = tree.resolve(node.to + 1, 1);
4733
+ if (next?.name === "TaskMarker") {
4734
+ break;
4735
+ }
4736
+ const label = list.type === "OrderedList" ? `${++list.number}.` : Unicode.bulletSmall;
4737
+ const line = state.doc.lineAt(node.from);
4738
+ const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
4739
+ atomicDeco.add(line.from, to, Decoration7.replace({
4740
+ widget: new TextWidget(label, list.type === "OrderedList" ? "cm-list-mark cm-list-mark-ordered" : "cm-list-mark cm-list-mark-bullet")
4741
+ }));
4742
+ break;
4743
+ }
4744
+ case "TaskMarker": {
4745
+ const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
4746
+ const line = state.doc.lineAt(node.from);
4747
+ const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
4748
+ atomicDeco.add(line.from, to, checked ? checkedTask : uncheckedTask);
4749
+ break;
4750
+ }
4751
+ //
4752
+ // Blockquote > QuoteMark > Paragraph
4753
+ //
4754
+ case "Blockquote": {
4755
+ const editing = editingRange(state, node, focus2);
4756
+ const quoteMark = node.node.getChild("QuoteMark");
4757
+ const paragraph = node.node.getChild("Paragraph");
4758
+ if (!editing && quoteMark && paragraph) {
4759
+ atomicDeco.add(quoteMark.from, paragraph.from, hide);
4760
+ }
4761
+ for (const block of view.viewportLineBlocks) {
4762
+ if (block.to < node.from) {
4763
+ continue;
4764
+ }
4765
+ if (block.from > node.to) {
4766
+ break;
4767
+ }
4768
+ deco.add(block.from, block.from, blockQuote);
4769
+ }
4770
+ break;
4771
+ }
4772
+ //
4773
+ // CommentBlock
4774
+ //
4775
+ case "CommentBlock": {
4776
+ const editing = editingRange(state, node, focus2);
4777
+ for (const block of view.viewportLineBlocks) {
4778
+ if (block.to < node.from) {
4779
+ continue;
4780
+ }
4781
+ if (block.from > node.to) {
4782
+ break;
4783
+ }
4784
+ const isFirst = block.from <= node.from;
4785
+ const isLast = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
4786
+ deco.add(block.from, block.from, isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine);
4787
+ if (!editing && (isFirst || isLast)) {
4788
+ atomicDeco.add(block.from, block.to, hide);
4789
+ }
4790
+ }
4791
+ break;
4792
+ }
4793
+ //
4794
+ // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
4795
+ //
4796
+ case "FencedCode": {
4797
+ for (const block of view.viewportLineBlocks) {
4798
+ if (block.to < node.from) {
4799
+ continue;
4800
+ }
4801
+ if (block.from > node.to) {
4802
+ break;
4803
+ }
4804
+ const first = block.from <= node.from;
4805
+ const last = block.to >= node.to && /```$/.test(state.doc.sliceString(block.from, block.to));
4806
+ deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
4807
+ const editing = editingRange(state, node, focus2);
4808
+ if (!editing && (first || last)) {
4809
+ atomicDeco.add(block.from, block.to, hide);
4810
+ }
4811
+ }
4812
+ return false;
4813
+ }
4814
+ //
4815
+ // Link > [LinkMark, URL]
4816
+ //
4817
+ case "Link": {
4818
+ const marks = node.node.getChildren("LinkMark");
4819
+ const urlNode = node.node.getChild("URL");
4820
+ const editing = editingRange(state, node, focus2);
4821
+ if (urlNode && marks.length >= 2) {
4822
+ const url = state.sliceDoc(urlNode.from, urlNode.to);
4823
+ if (!editing) {
4824
+ atomicDeco.add(node.from, marks[0].to, hide);
4825
+ }
4826
+ deco.add(marks[0].to, marks[1].from, Decoration7.mark({
4827
+ tagName: "a",
4828
+ attributes: {
4829
+ class: "cm-link",
4830
+ href: url,
4831
+ rel: "noreferrer",
4832
+ target: "_blank"
4833
+ }
4834
+ }));
4835
+ if (!editing) {
4836
+ atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration7.replace({
4837
+ widget: new LinkButton(url, options.renderLinkButton)
4838
+ }) : hide);
4839
+ }
4840
+ }
4841
+ break;
4842
+ }
4843
+ //
4844
+ // HR
4845
+ //
4846
+ case "HorizontalRule": {
4847
+ if (!editingRange(state, node, focus2)) {
4848
+ deco.add(node.from, node.to, horizontalRule);
4849
+ }
4850
+ break;
4851
+ }
4852
+ default: {
4853
+ if (autoHideTags.has(node.name)) {
4854
+ if (!editingRange(state, node.node.parent, focus2)) {
4855
+ atomicDeco.add(node.from, node.to, hide);
4856
+ }
4857
+ }
4858
+ }
4859
+ }
4860
+ };
4861
+ const leaveNode = (node) => {
4862
+ switch (node.name) {
4863
+ case "BulletList":
4864
+ case "OrderedList": {
4865
+ leaveList();
4866
+ break;
4867
+ }
4868
+ }
4869
+ };
4870
+ const tree = syntaxTree7(state);
4871
+ if (options.numberedHeadings?.from === void 0) {
4872
+ for (const { from, to } of view.visibleRanges) {
4873
+ tree.iterate({
4874
+ from,
4875
+ to,
4876
+ enter: wrapWithCatch(enterNode),
4877
+ leave: wrapWithCatch(leaveNode)
4878
+ });
4879
+ }
4880
+ } else {
4881
+ tree.iterate({
4882
+ enter: wrapWithCatch(enterNode),
4883
+ leave: wrapWithCatch(leaveNode)
4884
+ });
4885
+ }
4886
+ return {
4887
+ deco: deco.finish(),
4888
+ atomicDeco: atomicDeco.finish()
4889
+ };
4890
+ };
4891
+ var forceUpdate = StateEffect5.define();
4892
+ var decorateMarkdown = (options = {}) => {
4893
+ return [
4894
+ ViewPlugin7.fromClass(class {
4895
+ constructor(view) {
4896
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(view, options, view.hasFocus));
4897
+ }
4898
+ update(update2) {
4899
+ if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
4900
+ ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(update2.view, options, update2.view.hasFocus));
4901
+ this.clearUpdate();
4902
+ } else if (update2.selectionSet) {
4903
+ this.scheduleUpdate(update2.view);
4904
+ }
4905
+ }
4906
+ // Defer update in case moving through the document.
4907
+ scheduleUpdate(view) {
4908
+ this.clearUpdate();
4909
+ this.pendingUpdate = setTimeout(() => {
4910
+ view.dispatch({
4911
+ effects: forceUpdate.of(null)
4912
+ });
4913
+ }, options.selectionChangeDelay);
4914
+ }
4915
+ clearUpdate() {
4916
+ if (this.pendingUpdate) {
4917
+ clearTimeout(this.pendingUpdate);
4918
+ this.pendingUpdate = void 0;
4919
+ }
4920
+ }
4921
+ destroy() {
4922
+ this.clearUpdate();
4923
+ }
4924
+ }, {
4925
+ provide: (plugin) => [
4926
+ EditorView18.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4927
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
4928
+ EditorView18.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration7.none)
5007
4929
  ]
5008
- });
5009
- return listLevels[listLevels.length - 1];
4930
+ }),
4931
+ image(),
4932
+ table(),
4933
+ adjustChanges(),
4934
+ formattingStyles
4935
+ ];
4936
+ };
4937
+
4938
+ // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
4939
+ import { syntaxTree as syntaxTree8 } from "@codemirror/language";
4940
+ import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
4941
+ import { tooltipContent } from "@dxos/react-ui-theme";
4942
+ var linkTooltip = (renderTooltip) => {
4943
+ return hoverTooltip2((view, pos, side) => {
4944
+ const syntax = syntaxTree8(view.state).resolveInner(pos, side);
4945
+ let link = null;
4946
+ for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
4947
+ link = node.name === "Link" ? node : null;
4948
+ }
4949
+ const url = link && link.getChild("URL");
4950
+ if (!url || !link) {
4951
+ return null;
4952
+ }
4953
+ const urlText = view.state.sliceDoc(url.from, url.to);
4954
+ return {
4955
+ pos: link.from,
4956
+ end: link.to,
4957
+ // NOTE: Forcing above causes the tooltip to flicker.
4958
+ // above: true,
4959
+ create: () => {
4960
+ const el = document.createElement("div");
4961
+ el.className = tooltipContent({});
4962
+ renderTooltip(el, {
4963
+ url: urlText
4964
+ }, view);
4965
+ return {
4966
+ dom: el,
4967
+ offset: {
4968
+ x: 0,
4969
+ y: 4
4970
+ }
4971
+ };
4972
+ }
4973
+ };
4974
+ }, {
4975
+ // NOTE: 0 = default of 300ms.
4976
+ hoverTime: 1
4977
+ });
4978
+ };
4979
+
4980
+ // packages/ui/react-ui-editor/src/extensions/markdown/outliner.ts
4981
+ import { syntaxTree as syntaxTree9 } from "@codemirror/language";
4982
+ import { StateField as StateField10, EditorState as EditorState2 } from "@codemirror/state";
4983
+ import { Decoration as Decoration8, EditorView as EditorView19 } from "@codemirror/view";
4984
+ import { log as log6 } from "@dxos/log";
4985
+ import { mx as mx3 } from "@dxos/react-ui-theme";
4986
+ var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/markdown/outliner.ts";
4987
+ var indentLevel = 2;
4988
+ var matchTaskMarker = /^\s*- (\[ \]|\[x\])? /;
4989
+ var getLineInfo = (line) => {
4990
+ const match = line.text.match(matchTaskMarker);
4991
+ const start = line.from + (match?.[0]?.length ?? 0);
4992
+ return {
4993
+ match,
4994
+ start
5010
4995
  };
5011
- const enterNode = (node) => {
5012
- switch (node.name) {
5013
- // ATXHeading > HeaderMark > Paragraph
5014
- // NOTE: Numbering requires processing the entire document since otherwise only the visible range will be
5015
- // processed and the numbering will be incorrect.
5016
- case "ATXHeading1":
5017
- case "ATXHeading2":
5018
- case "ATXHeading3":
5019
- case "ATXHeading4":
5020
- case "ATXHeading5":
5021
- case "ATXHeading6": {
5022
- const level = parseInt(node.name["ATXHeading".length]);
5023
- const headers = getHeaderLevels(node, level);
5024
- if (options.numberedHeadings?.from !== void 0) {
5025
- const header = headers[level - 1];
5026
- if (header) {
5027
- header.number++;
4996
+ };
4997
+ var outliner = () => [
4998
+ EditorState2.transactionFilter.of((tr) => {
4999
+ if (!tr.docChanged) {
5000
+ const pos = tr.selection?.ranges[tr.selection?.mainIndex]?.from;
5001
+ if (pos != null) {
5002
+ const { match, start } = getLineInfo(tr.startState.doc.lineAt(pos));
5003
+ if (match) {
5004
+ if (pos < start) {
5005
+ return [
5006
+ {
5007
+ selection: {
5008
+ anchor: start,
5009
+ head: start
5010
+ }
5011
+ }
5012
+ ];
5028
5013
  }
5029
5014
  }
5030
- const editing = editingRange(state, node, focus2);
5031
- if (editing) {
5032
- break;
5015
+ }
5016
+ return tr;
5017
+ }
5018
+ const changes = [];
5019
+ tr.changes.iterChanges((fromA, toA, fromB, toB, insert) => {
5020
+ const line = tr.startState.doc.lineAt(fromA);
5021
+ const isTaskMarker = line.text.match(matchTaskMarker);
5022
+ if (isTaskMarker) {
5023
+ const { start } = getLineInfo(line);
5024
+ const replace = start === toA && toA - fromA === insert.length;
5025
+ if (replace) {
5026
+ log6.info("delete line", void 0, {
5027
+ F: __dxlog_file10,
5028
+ L: 82,
5029
+ S: void 0,
5030
+ C: (f, a) => f(...a)
5031
+ });
5032
+ changes.push({
5033
+ from: line.from - 1,
5034
+ to: toA
5035
+ });
5036
+ return;
5033
5037
  }
5034
- const mark = node.node.firstChild;
5035
- if (mark?.name === "HeaderMark") {
5036
- const { from, to = 6 } = options.numberedHeadings ?? {};
5037
- const text = view.state.sliceDoc(node.from, node.to);
5038
- const len = text.match(/[#\s]+/)[0].length;
5039
- if (!from || level < from || level > to) {
5040
- atomicDeco.add(mark.from, mark.from + len, hide);
5038
+ if (fromB === toB) {
5039
+ if (toA === line.to) {
5040
+ const line2 = tr.state.doc.lineAt(fromA);
5041
+ if (line2.text.match(/^\s*$/)) {
5042
+ if (line2.from === 0) {
5043
+ log6.info("skip", void 0, {
5044
+ F: __dxlog_file10,
5045
+ L: 94,
5046
+ S: void 0,
5047
+ C: (f, a) => f(...a)
5048
+ });
5049
+ changes.push({
5050
+ from: 0,
5051
+ to: 0
5052
+ });
5053
+ return;
5054
+ } else {
5055
+ log6.info("delete line", void 0, {
5056
+ F: __dxlog_file10,
5057
+ L: 99,
5058
+ S: void 0,
5059
+ C: (f, a) => f(...a)
5060
+ });
5061
+ changes.push({
5062
+ from: line2.from - 1,
5063
+ to: toA
5064
+ });
5065
+ return;
5066
+ }
5067
+ }
5068
+ }
5069
+ return;
5070
+ }
5071
+ if (insert.length === indentLevel) {
5072
+ if (line.number === 1) {
5073
+ log6.info("skip", void 0, {
5074
+ F: __dxlog_file10,
5075
+ L: 111,
5076
+ S: void 0,
5077
+ C: (f, a) => f(...a)
5078
+ });
5079
+ changes.push({
5080
+ from: 0,
5081
+ to: 0
5082
+ });
5083
+ return;
5041
5084
  } else {
5042
- const num = headers.slice(from - 1).map((level2) => level2?.number ?? 0).join(".") + " ";
5043
- if (num.length) {
5044
- atomicDeco.add(mark.from, mark.from + len, Decoration7.replace({
5045
- widget: new TextWidget(num, theme.heading(level))
5046
- }));
5085
+ const getIndent = (text) => (text.match(/^\s*/)?.[0]?.length ?? 0) / indentLevel;
5086
+ const currentIndent = getIndent(line.text);
5087
+ const indentPrevious = getIndent(tr.state.doc.lineAt(fromA - 1).text);
5088
+ if (currentIndent > indentPrevious) {
5089
+ log6.info("skip", void 0, {
5090
+ F: __dxlog_file10,
5091
+ L: 119,
5092
+ S: void 0,
5093
+ C: (f, a) => f(...a)
5094
+ });
5095
+ changes.push({
5096
+ from: 0,
5097
+ to: 0
5098
+ });
5099
+ return;
5047
5100
  }
5048
5101
  }
5049
5102
  }
5050
- return false;
5103
+ log6.info("change", {
5104
+ line: {
5105
+ from: line.from,
5106
+ to: line.to
5107
+ },
5108
+ start,
5109
+ a: [
5110
+ fromA,
5111
+ toA
5112
+ ],
5113
+ b: [
5114
+ fromB,
5115
+ toB
5116
+ ],
5117
+ insert: {
5118
+ text: insert.toString(),
5119
+ length: insert.length
5120
+ }
5121
+ }, {
5122
+ F: __dxlog_file10,
5123
+ L: 134,
5124
+ S: void 0,
5125
+ C: (f, a) => f(...a)
5126
+ });
5051
5127
  }
5052
- //
5053
- // Lists.
5054
- // [BulletList | OrderedList] > (ListItem > ListMark) > (Task > TaskMarker)?
5055
- //
5056
- case "BulletList":
5057
- case "OrderedList": {
5058
- enterList(node);
5059
- break;
5128
+ });
5129
+ if (changes.length > 0) {
5130
+ return [
5131
+ {
5132
+ changes
5133
+ }
5134
+ ];
5135
+ }
5136
+ return tr;
5137
+ }),
5138
+ StateField10.define({
5139
+ create: (state) => {
5140
+ return Decoration8.set(buildDecorations3(0, state.doc.length, state));
5141
+ },
5142
+ update: (value, tr) => {
5143
+ const from = 0;
5144
+ const to = tr.state.doc.length;
5145
+ return value.map(tr.changes).update({
5146
+ filterFrom: 0,
5147
+ filterTo: tr.state.doc.length,
5148
+ filter: () => false,
5149
+ add: buildDecorations3(from, to, tr.state)
5150
+ });
5151
+ },
5152
+ provide: (field) => EditorView19.decorations.from(field)
5153
+ }),
5154
+ // TODO(burdon): Increase indent padding by configuring decorate extension.
5155
+ // TODO(burdon): Hover to select entire group.
5156
+ EditorView19.theme({
5157
+ ".cm-list-item-start": {
5158
+ borderTop: "1px solid var(--dx-separator)",
5159
+ borderLeft: "1px solid var(--dx-separator)",
5160
+ borderRight: "1px solid var(--dx-separator)",
5161
+ borderTopLeftRadius: "4px",
5162
+ borderTopRightRadius: "4px",
5163
+ paddingTop: "4px",
5164
+ marginTop: "8px"
5165
+ },
5166
+ ".cm-list-item-end": {
5167
+ borderLeft: "1px solid var(--dx-separator)",
5168
+ borderRight: "1px solid var(--dx-separator)",
5169
+ borderBottom: "1px solid var(--dx-separator)",
5170
+ borderBottomLeftRadius: "4px",
5171
+ borderBottomRightRadius: "4px",
5172
+ paddingBottom: "4px",
5173
+ marginBottom: "8px"
5174
+ },
5175
+ ".cm-list-item-continuation": {
5176
+ borderLeft: "1px solid var(--dx-separator)",
5177
+ borderRight: "1px solid var(--dx-separator)",
5178
+ // TODO(burdon): Should match parent indentation.
5179
+ paddingLeft: "24px"
5180
+ },
5181
+ // TODO(burdon): Set via options to decorate extension.
5182
+ ".cm-list-item-continuation.cm-codeblock-start": {
5183
+ borderRadius: "0"
5184
+ }
5185
+ })
5186
+ ];
5187
+ var buildDecorations3 = (from, to, state) => {
5188
+ const decorations = [];
5189
+ syntaxTree9(state).iterate({
5190
+ enter: (node) => {
5191
+ if (node.name === "ListItem") {
5192
+ const sub = node.node.getChild("BulletList");
5193
+ const lineStart = state.doc.lineAt(node.from);
5194
+ const lineEnd = sub ? state.doc.lineAt(state.doc.lineAt(sub.from).from - 1) : state.doc.lineAt(node.to);
5195
+ decorations.push(Decoration8.line({
5196
+ class: mx3("cm-list-item-start", lineStart.number === lineEnd.number && "cm-list-item-end")
5197
+ }).range(lineStart.from, lineStart.from));
5198
+ for (let i = lineStart.from + 1; i < lineEnd.from; i++) {
5199
+ decorations.push(Decoration8.line({
5200
+ class: mx3("cm-list-item-continuation")
5201
+ }).range(i, i));
5202
+ }
5203
+ if (lineStart.number !== lineEnd.number) {
5204
+ decorations.push(Decoration8.line({
5205
+ class: mx3("cm-list-item-end")
5206
+ }).range(lineEnd.from, lineEnd.from));
5207
+ }
5060
5208
  }
5061
- case "ListItem": {
5062
- const line = state.doc.lineAt(node.from);
5063
- const list = getCurrentListLevel();
5064
- const width = list.type === "OrderedList" ? orderedListIndentationWidth : bulletListIndentationWidth;
5065
- const offset = ((list.level ?? 0) + 1) * width;
5066
- if (node.from === line.to - 1) {
5067
- return false;
5209
+ }
5210
+ });
5211
+ return decorations;
5212
+ };
5213
+
5214
+ // packages/ui/react-ui-editor/src/extensions/mention.ts
5215
+ import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
5216
+ import { log as log7 } from "@dxos/log";
5217
+ var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
5218
+ var mention = ({ debug, onSearch }) => {
5219
+ return autocompletion2({
5220
+ // TODO(burdon): Not working.
5221
+ activateOnTyping: true,
5222
+ // activateOnTypingDelay: 100,
5223
+ // selectOnOpen: true,
5224
+ closeOnBlur: !debug,
5225
+ // defaultKeymap: false,
5226
+ icons: false,
5227
+ override: [
5228
+ (context) => {
5229
+ log7.info("completion context", {
5230
+ context
5231
+ }, {
5232
+ F: __dxlog_file11,
5233
+ L: 27,
5234
+ S: void 0,
5235
+ C: (f, a) => f(...a)
5236
+ });
5237
+ const match = context.matchBefore(/@(\w+)?/);
5238
+ if (!match || match.from === match.to && !context.explicit) {
5239
+ return null;
5068
5240
  }
5069
- deco.add(line.from, line.from, Decoration7.line({
5070
- class: "cm-list-item",
5071
- attributes: {
5072
- style: `padding-left: ${offset}px; text-indent: -${width}px;`
5073
- }
5074
- }));
5075
- break;
5241
+ return {
5242
+ from: match.from,
5243
+ options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5244
+ label: `@${value}`
5245
+ }))
5246
+ };
5076
5247
  }
5077
- case "ListMark": {
5078
- const list = getCurrentListLevel();
5079
- const next = tree.resolve(node.to + 1, 1);
5080
- if (next?.name === "TaskMarker") {
5081
- break;
5248
+ ]
5249
+ });
5250
+ };
5251
+
5252
+ // packages/ui/react-ui-editor/src/extensions/modes.ts
5253
+ import { keymap as keymap9 } from "@codemirror/view";
5254
+ import { vim } from "@replit/codemirror-vim";
5255
+ import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5256
+ import { Schema } from "effect";
5257
+ var EditorViewModes = [
5258
+ "preview",
5259
+ "readonly",
5260
+ "source"
5261
+ ];
5262
+ var EditorViewMode = Schema.Union(...EditorViewModes.map((mode) => Schema.Literal(mode)));
5263
+ var EditorInputModes = [
5264
+ "default",
5265
+ "vim",
5266
+ "vscode"
5267
+ ];
5268
+ var EditorInputMode = Schema.Union(...EditorInputModes.map((mode) => Schema.Literal(mode)));
5269
+ var editorInputMode = singleValueFacet({});
5270
+ var InputModeExtensions = {
5271
+ default: [],
5272
+ vscode: [
5273
+ // https://github.com/replit/codemirror-vscode-keymap
5274
+ editorInputMode.of({
5275
+ type: "vscode"
5276
+ }),
5277
+ keymap9.of(vscodeKeymap)
5278
+ ],
5279
+ vim: [
5280
+ // https://github.com/replit/codemirror-vim
5281
+ vim(),
5282
+ editorInputMode.of({
5283
+ type: "vim",
5284
+ noTabster: true
5285
+ }),
5286
+ keymap9.of([
5287
+ {
5288
+ key: "Alt-Escape",
5289
+ run: (view) => {
5290
+ view.dom.parentElement?.focus();
5291
+ return true;
5082
5292
  }
5083
- const label = list.type === "OrderedList" ? `${++list.number}.` : Unicode.bulletSmall;
5084
- const line = state.doc.lineAt(node.from);
5085
- const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
5086
- atomicDeco.add(line.from, to, Decoration7.replace({
5087
- widget: new TextWidget(label, list.type === "OrderedList" ? "cm-list-mark cm-list-mark-ordered" : "cm-list-mark cm-list-mark-bullet")
5088
- }));
5089
- break;
5090
- }
5091
- case "TaskMarker": {
5092
- const checked = state.doc.sliceString(node.from + 1, node.to - 1) === "x";
5093
- const line = state.doc.lineAt(node.from);
5094
- const to = state.doc.sliceString(node.to, node.to + 1) === " " ? node.to + 1 : node.to;
5095
- atomicDeco.add(line.from, to, checked ? checkedTask : uncheckedTask);
5096
- break;
5097
5293
  }
5098
- //
5099
- // Blockquote > QuoteMark > Paragraph
5100
- //
5101
- case "Blockquote": {
5102
- const editing = editingRange(state, node, focus2);
5103
- const quoteMark = node.node.getChild("QuoteMark");
5104
- const paragraph = node.node.getChild("Paragraph");
5105
- if (!editing && quoteMark && paragraph) {
5106
- atomicDeco.add(quoteMark.from, paragraph.from, hide);
5107
- }
5108
- for (const block of view.viewportLineBlocks) {
5109
- if (block.to < node.from) {
5110
- continue;
5111
- }
5112
- if (block.from > node.to) {
5113
- break;
5114
- }
5115
- deco.add(block.from, block.from, blockQuote);
5116
- }
5117
- break;
5294
+ ])
5295
+ ]
5296
+ };
5297
+
5298
+ // packages/ui/react-ui-editor/src/extensions/preview/preview.ts
5299
+ import "@dxos/lit-ui/dx-ref-tag.pcss";
5300
+ import { syntaxTree as syntaxTree10 } from "@codemirror/language";
5301
+ import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField11 } from "@codemirror/state";
5302
+ import { Decoration as Decoration9, EditorView as EditorView20, WidgetType as WidgetType6 } from "@codemirror/view";
5303
+ var preview = (options = {}) => {
5304
+ return [
5305
+ // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
5306
+ // "Block decorations may not be specified via plugins"
5307
+ StateField11.define({
5308
+ create: (state) => buildDecorations4(state, options),
5309
+ update: (_, tr) => buildDecorations4(tr.state, options),
5310
+ provide: (field) => [
5311
+ EditorView20.decorations.from(field),
5312
+ EditorView20.atomicRanges.of((view) => view.state.field(field))
5313
+ ]
5314
+ }),
5315
+ EditorView20.theme({
5316
+ ".cm-preview-block": {
5317
+ marginLeft: "-1rem",
5318
+ marginRight: "-1rem",
5319
+ padding: "1rem",
5320
+ borderRadius: "0.5rem",
5321
+ background: "var(--dx-modalSurface)",
5322
+ border: "1px solid var(--dx-separator)"
5118
5323
  }
5119
- //
5120
- // CommentBlock
5121
- //
5122
- case "CommentBlock": {
5123
- const editing = editingRange(state, node, focus2);
5124
- for (const block of view.viewportLineBlocks) {
5125
- if (block.to < node.from) {
5126
- continue;
5127
- }
5128
- if (block.from > node.to) {
5129
- break;
5130
- }
5131
- const isFirst = block.from <= node.from;
5132
- const isLast = block.to >= node.to && /^(\s>)*-->$/.test(state.doc.sliceString(block.from, block.to));
5133
- deco.add(block.from, block.from, isFirst ? commentBlockLineFirst : isLast ? commentBlockLineLast : commentBlockLine);
5134
- if (!editing && (isFirst || isLast)) {
5135
- atomicDeco.add(block.from, block.to, hide);
5324
+ })
5325
+ ];
5326
+ };
5327
+ var getLinkRef = (state, node) => {
5328
+ const mark = node.getChild("LinkMark");
5329
+ const label = node.getChild("LinkLabel");
5330
+ if (mark && label) {
5331
+ const ref = state.sliceDoc(label.from + 1, label.to - 1);
5332
+ return {
5333
+ suggest: ref.startsWith("?"),
5334
+ block: state.sliceDoc(mark.from, mark.from + 1) === "!",
5335
+ label: state.sliceDoc(mark.to, label.from - 1),
5336
+ ref: ref.startsWith("?") ? ref.slice(1) : ref
5337
+ };
5338
+ }
5339
+ };
5340
+ var buildDecorations4 = (state, options) => {
5341
+ const builder = new RangeSetBuilder4();
5342
+ syntaxTree10(state).iterate({
5343
+ enter: (node) => {
5344
+ switch (node.name) {
5345
+ //
5346
+ // Decoration.
5347
+ // [Label][dxn:echo:123]
5348
+ //
5349
+ case "Link": {
5350
+ const link = getLinkRef(state, node.node);
5351
+ if (link) {
5352
+ builder.add(node.from, node.to, Decoration9.replace({
5353
+ widget: new PreviewInlineWidget(options, link)
5354
+ }));
5136
5355
  }
5356
+ break;
5137
5357
  }
5138
- break;
5139
- }
5140
- //
5141
- // FencedCode > CodeMark > [CodeInfo] > CodeText > CodeMark
5142
- //
5143
- case "FencedCode": {
5144
- for (const block of view.viewportLineBlocks) {
5145
- if (block.to < node.from) {
5146
- continue;
5147
- }
5148
- if (block.from > node.to) {
5149
- break;
5150
- }
5151
- const first = block.from <= node.from;
5152
- const last = block.to >= node.to && /^(\s>)*```$/.test(state.doc.sliceString(block.from, block.to));
5153
- deco.add(block.from, block.from, first ? fencedCodeLineFirst : last ? fencedCodeLineLast : fencedCodeLine);
5154
- const editing = editingRange(state, node, focus2);
5155
- if (!editing && (first || last)) {
5156
- atomicDeco.add(block.from, block.to, hide);
5358
+ //
5359
+ // Block widget.
5360
+ // ![Label][dxn:echo:123]
5361
+ //
5362
+ case "Image": {
5363
+ const link = getLinkRef(state, node.node);
5364
+ if (options.renderBlock && link) {
5365
+ builder.add(node.from, node.to, Decoration9.replace({
5366
+ block: true,
5367
+ // atomic: true,
5368
+ widget: new PreviewBlockWidget(options, link)
5369
+ }));
5157
5370
  }
5371
+ break;
5158
5372
  }
5159
- return false;
5160
5373
  }
5161
- //
5162
- // Link > [LinkMark, URL]
5163
- //
5164
- case "Link": {
5165
- const marks = node.node.getChildren("LinkMark");
5166
- const urlNode = node.node.getChild("URL");
5167
- const editing = editingRange(state, node, focus2);
5168
- if (urlNode && marks.length >= 2) {
5169
- const url = state.sliceDoc(urlNode.from, urlNode.to);
5170
- if (!editing) {
5171
- atomicDeco.add(node.from, marks[0].to, hide);
5172
- }
5173
- deco.add(marks[0].to, marks[1].from, Decoration7.mark({
5174
- tagName: "a",
5175
- attributes: {
5176
- class: "cm-link",
5177
- href: url,
5178
- rel: "noreferrer",
5179
- target: "_blank"
5374
+ }
5375
+ });
5376
+ return builder.finish();
5377
+ };
5378
+ var PreviewInlineWidget = class extends WidgetType6 {
5379
+ constructor(_options, _link) {
5380
+ super();
5381
+ this._options = _options;
5382
+ this._link = _link;
5383
+ }
5384
+ // override ignoreEvent() {
5385
+ // return false;
5386
+ // }
5387
+ eq(other) {
5388
+ return this._link.ref === other._link.ref && this._link.label === other._link.label;
5389
+ }
5390
+ toDOM(view) {
5391
+ const root = document.createElement("dx-ref-tag");
5392
+ root.textContent = this._link.label;
5393
+ root.setAttribute("ref", this._link.ref);
5394
+ return root;
5395
+ }
5396
+ };
5397
+ var PreviewBlockWidget = class extends WidgetType6 {
5398
+ constructor(_options, _link) {
5399
+ super();
5400
+ this._options = _options;
5401
+ this._link = _link;
5402
+ }
5403
+ // override ignoreEvent() {
5404
+ // return true;
5405
+ // }
5406
+ eq(other) {
5407
+ return this._link.ref === other._link.ref;
5408
+ }
5409
+ toDOM(view) {
5410
+ const root = document.createElement("div");
5411
+ root.classList.add("cm-preview-block");
5412
+ const handleAction = (action) => {
5413
+ const pos = view.posAtDOM(root);
5414
+ const node = syntaxTree10(view.state).resolve(pos + 1).node.parent;
5415
+ if (!node) {
5416
+ return;
5417
+ }
5418
+ const link = getLinkRef(view.state, node);
5419
+ if (link?.ref !== action.link.ref) {
5420
+ return;
5421
+ }
5422
+ switch (action.type) {
5423
+ // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
5424
+ // Insert ref text.
5425
+ case "insert": {
5426
+ view.dispatch({
5427
+ changes: {
5428
+ from: node.from,
5429
+ to: node.to,
5430
+ insert: action.target.text
5180
5431
  }
5181
- }));
5182
- if (!editing) {
5183
- atomicDeco.add(marks[1].from, node.to, options.renderLinkButton ? Decoration7.replace({
5184
- widget: new LinkButton(url, options.renderLinkButton)
5185
- }) : hide);
5186
- }
5432
+ });
5433
+ break;
5187
5434
  }
5188
- break;
5189
- }
5190
- //
5191
- // HR
5192
- //
5193
- case "HorizontalRule": {
5194
- if (!editingRange(state, node, focus2)) {
5195
- deco.add(node.from, node.to, horizontalRule);
5435
+ // Remove ref.
5436
+ case "delete": {
5437
+ view.dispatch({
5438
+ changes: {
5439
+ from: node.from,
5440
+ to: node.to
5441
+ }
5442
+ });
5443
+ break;
5196
5444
  }
5197
- break;
5198
5445
  }
5199
- default: {
5200
- if (autoHideTags.has(node.name)) {
5201
- if (!editingRange(state, node.node.parent, focus2)) {
5202
- atomicDeco.add(node.from, node.to, hide);
5446
+ };
5447
+ this._options.renderBlock(root, {
5448
+ readonly: view.state.readOnly,
5449
+ link: this._link,
5450
+ onAction: handleAction,
5451
+ onLookup: this._options.onLookup
5452
+ }, view);
5453
+ return root;
5454
+ }
5455
+ };
5456
+
5457
+ // packages/ui/react-ui-editor/src/extensions/typewriter.ts
5458
+ import { keymap as keymap10 } from "@codemirror/view";
5459
+ var defaultItems = [
5460
+ "hello world!",
5461
+ "this is a test.",
5462
+ "this is [DXOS](https://dxos.org)"
5463
+ ];
5464
+ var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
5465
+ let t;
5466
+ let idx = 0;
5467
+ return [
5468
+ keymap10.of([
5469
+ {
5470
+ // Reset.
5471
+ key: "alt-meta-'",
5472
+ run: (view) => {
5473
+ clearTimeout(t);
5474
+ idx = 0;
5475
+ return true;
5476
+ }
5477
+ },
5478
+ {
5479
+ // Next prompt.
5480
+ // TODO(burdon): Press 1-9 to select prompt?
5481
+ key: "shift-meta-'",
5482
+ run: (view) => {
5483
+ clearTimeout(t);
5484
+ const text = items[idx++];
5485
+ if (idx === items?.length) {
5486
+ idx = 0;
5203
5487
  }
5488
+ let i = 0;
5489
+ const insert = (d = 0) => {
5490
+ t = setTimeout(() => {
5491
+ const pos = view.state.selection.main.head;
5492
+ view.dispatch({
5493
+ changes: {
5494
+ from: pos,
5495
+ insert: text[i++]
5496
+ },
5497
+ selection: {
5498
+ anchor: pos + 1
5499
+ }
5500
+ });
5501
+ if (i < text.length) {
5502
+ insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
5503
+ }
5504
+ }, d);
5505
+ };
5506
+ insert();
5507
+ return true;
5204
5508
  }
5205
5509
  }
5510
+ ])
5511
+ ];
5512
+ };
5513
+
5514
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/blocks.ts
5515
+ var createBlockGroupAction = (value) => createEditorActionGroup("block", {
5516
+ variant: "toggleGroup",
5517
+ selectCardinality: "single",
5518
+ value
5519
+ });
5520
+ var createBlockActions = (value, getView, blankLine) => Object.entries({
5521
+ blockquote: "ph--quotes--regular",
5522
+ codeblock: "ph--code-block--regular",
5523
+ table: "ph--table--regular"
5524
+ }).map(([type, icon]) => {
5525
+ const checked = type === value;
5526
+ return createEditorAction(type, () => {
5527
+ const view = getView();
5528
+ if (!view) {
5529
+ return;
5206
5530
  }
5207
- };
5208
- const leaveNode = (node) => {
5209
- switch (node.name) {
5210
- case "BulletList":
5211
- case "OrderedList": {
5212
- leaveList();
5531
+ switch (type) {
5532
+ case "blockquote":
5533
+ checked ? removeBlockquote(view) : addBlockquote(view);
5534
+ break;
5535
+ case "codeblock":
5536
+ checked ? removeCodeblock(view) : addCodeblock(view);
5537
+ break;
5538
+ case "table":
5539
+ insertTable(view);
5213
5540
  break;
5214
- }
5215
- }
5216
- };
5217
- const tree = syntaxTree7(state);
5218
- if (options.numberedHeadings?.from === void 0) {
5219
- for (const { from, to } of view.visibleRanges) {
5220
- tree.iterate({
5221
- from,
5222
- to,
5223
- enter: wrapWithCatch(enterNode),
5224
- leave: wrapWithCatch(leaveNode)
5225
- });
5226
5541
  }
5227
- } else {
5228
- tree.iterate({
5229
- enter: wrapWithCatch(enterNode),
5230
- leave: wrapWithCatch(leaveNode)
5231
- });
5232
- }
5542
+ }, {
5543
+ checked,
5544
+ ...type === "table" && {
5545
+ disabled: !!blankLine
5546
+ },
5547
+ icon
5548
+ });
5549
+ });
5550
+ var createBlocks = (state, getView) => {
5551
+ const value = state?.blockQuote ? "blockquote" : state.blockType ?? "";
5552
+ const blockGroupAction = createBlockGroupAction(value);
5553
+ const blockActions = createBlockActions(value, getView, state.blankLine);
5233
5554
  return {
5234
- deco: deco.finish(),
5235
- atomicDeco: atomicDeco.finish()
5555
+ nodes: [
5556
+ blockGroupAction,
5557
+ ...blockActions
5558
+ ],
5559
+ edges: [
5560
+ {
5561
+ source: "root",
5562
+ target: "block"
5563
+ },
5564
+ ...blockActions.map(({ id }) => ({
5565
+ source: blockGroupAction.id,
5566
+ target: id
5567
+ }))
5568
+ ]
5236
5569
  };
5237
5570
  };
5238
- var forceUpdate = StateEffect5.define();
5239
- var decorateMarkdown = (options = {}) => {
5240
- return [
5241
- ViewPlugin7.fromClass(class {
5242
- constructor(view) {
5243
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(view, options, view.hasFocus));
5244
- }
5245
- update(update2) {
5246
- if (update2.docChanged || update2.viewportChanged || update2.focusChanged || update2.transactions.some((tr) => tr.effects.some((effect) => effect.is(forceUpdate))) || update2.selectionSet && !options.selectionChangeDelay) {
5247
- ({ deco: this.deco, atomicDeco: this.atomicDeco } = buildDecorations2(update2.view, options, update2.view.hasFocus));
5248
- this.clearUpdate();
5249
- } else if (update2.selectionSet) {
5250
- this.scheduleUpdate(update2.view);
5251
- }
5252
- }
5253
- // Defer update in case moving through the document.
5254
- scheduleUpdate(view) {
5255
- this.clearUpdate();
5256
- this.pendingUpdate = setTimeout(() => {
5257
- view.dispatch({
5258
- effects: forceUpdate.of(null)
5259
- });
5260
- }, options.selectionChangeDelay);
5261
- }
5262
- clearUpdate() {
5263
- if (this.pendingUpdate) {
5264
- clearTimeout(this.pendingUpdate);
5265
- this.pendingUpdate = void 0;
5266
- }
5267
- }
5268
- destroy() {
5269
- this.clearUpdate();
5270
- }
5271
- }, {
5272
- provide: (plugin) => [
5273
- EditorView19.atomicRanges.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
5274
- EditorView19.decorations.of((view) => view.plugin(plugin)?.atomicDeco ?? Decoration7.none),
5275
- EditorView19.decorations.of((view) => view.plugin(plugin)?.deco ?? Decoration7.none)
5276
- ]
5277
- }),
5278
- image(),
5279
- table(),
5280
- adjustChanges(),
5281
- formattingStyles
5282
- ];
5283
- };
5284
5571
 
5285
- // packages/ui/react-ui-editor/src/extensions/markdown/link.ts
5286
- import { syntaxTree as syntaxTree8 } from "@codemirror/language";
5287
- import { hoverTooltip as hoverTooltip2 } from "@codemirror/view";
5288
- import { tooltipContent } from "@dxos/react-ui-theme";
5289
- var linkTooltip = (renderTooltip) => {
5290
- return hoverTooltip2((view, pos, side) => {
5291
- const syntax = syntaxTree8(view.state).resolveInner(pos, side);
5292
- let link = null;
5293
- for (let i = 0, node = syntax; !link && node && i < 5; node = node.parent, i++) {
5294
- link = node.name === "Link" ? node : null;
5572
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/comment.ts
5573
+ var commentLabel = (comment, selection) => comment ? "selection overlaps existing comment label" : selection === false ? "select text to comment label" : "comment label";
5574
+ var createCommentAction = (label, getView) => createEditorAction("comment", () => createComment(getView()), {
5575
+ testId: "editor.toolbar.comment",
5576
+ icon: "ph--chat-text--regular",
5577
+ label
5578
+ });
5579
+ var createComment2 = (state, getView) => ({
5580
+ nodes: [
5581
+ createCommentAction([
5582
+ commentLabel(state.comment, state.selection),
5583
+ {
5584
+ ns: translationKey
5585
+ }
5586
+ ], getView)
5587
+ ],
5588
+ edges: [
5589
+ {
5590
+ source: "root",
5591
+ target: "comment"
5295
5592
  }
5296
- const url = link && link.getChild("URL");
5297
- if (!url || !link) {
5298
- return null;
5593
+ ]
5594
+ });
5595
+
5596
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/formatting.ts
5597
+ var formats = {
5598
+ strong: "ph--text-b--regular",
5599
+ emphasis: "ph--text-italic--regular",
5600
+ strikethrough: "ph--text-strikethrough--regular",
5601
+ code: "ph--code--regular",
5602
+ link: "ph--link--regular"
5603
+ };
5604
+ var createFormattingGroup = (formatting) => createEditorActionGroup("formatting", {
5605
+ variant: "toggleGroup",
5606
+ selectCardinality: "multiple",
5607
+ value: Object.keys(formats).filter((key) => !!formatting[key])
5608
+ });
5609
+ var createFormattingActions = (formatting, getView) => Object.entries(formats).map(([type, icon]) => {
5610
+ const checked = !!formatting[type];
5611
+ return createEditorAction(type, () => {
5612
+ const view = getView();
5613
+ if (!view) {
5614
+ return;
5299
5615
  }
5300
- const urlText = view.state.sliceDoc(url.from, url.to);
5301
- return {
5302
- pos: link.from,
5303
- end: link.to,
5304
- // NOTE: Forcing above causes the tooltip to flicker.
5305
- // above: true,
5306
- create: () => {
5307
- const el = document.createElement("div");
5308
- el.className = tooltipContent({});
5309
- renderTooltip(el, {
5310
- url: urlText
5311
- }, view);
5312
- return {
5313
- dom: el,
5314
- offset: {
5315
- x: 0,
5316
- y: 4
5317
- }
5318
- };
5319
- }
5320
- };
5616
+ if (type === "link") {
5617
+ checked ? removeLink(view) : addLink()(view);
5618
+ return;
5619
+ }
5620
+ const inlineType = type === "strong" ? Inline.Strong : type === "emphasis" ? Inline.Emphasis : type === "strikethrough" ? Inline.Strikethrough : Inline.Code;
5621
+ setStyle(inlineType, !checked)(view);
5321
5622
  }, {
5322
- // NOTE: 0 = default of 300ms.
5323
- hoverTime: 1
5623
+ checked,
5624
+ icon
5324
5625
  });
5626
+ });
5627
+ var createFormatting = (state, getView) => {
5628
+ const formattingGroupAction = createFormattingGroup(state);
5629
+ const formattingActions = createFormattingActions(state, getView);
5630
+ return {
5631
+ nodes: [
5632
+ formattingGroupAction,
5633
+ ...formattingActions
5634
+ ],
5635
+ edges: [
5636
+ {
5637
+ source: "root",
5638
+ target: "formatting"
5639
+ },
5640
+ ...formattingActions.map(({ id }) => ({
5641
+ source: formattingGroupAction.id,
5642
+ target: id
5643
+ }))
5644
+ ]
5645
+ };
5325
5646
  };
5326
5647
 
5327
- // packages/ui/react-ui-editor/src/extensions/mention.ts
5328
- import { autocompletion as autocompletion2 } from "@codemirror/autocomplete";
5329
- import { log as log6 } from "@dxos/log";
5330
- var __dxlog_file10 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/extensions/mention.ts";
5331
- var mention = ({ debug, onSearch }) => {
5332
- return autocompletion2({
5333
- // TODO(burdon): Not working.
5334
- activateOnTyping: true,
5335
- // activateOnTypingDelay: 100,
5336
- // selectOnOpen: true,
5337
- closeOnBlur: !debug,
5338
- // defaultKeymap: false,
5339
- icons: false,
5340
- override: [
5341
- (context) => {
5342
- log6.info("completion context", {
5343
- context
5344
- }, {
5345
- F: __dxlog_file10,
5346
- L: 27,
5347
- S: void 0,
5348
- C: (f, a) => f(...a)
5349
- });
5350
- const match = context.matchBefore(/@(\w+)?/);
5351
- if (!match || match.from === match.to && !context.explicit) {
5352
- return null;
5353
- }
5354
- return {
5355
- from: match.from,
5356
- options: onSearch(match.text.slice(1).toLowerCase()).map((value) => ({
5357
- label: `@${value}`
5358
- }))
5359
- };
5648
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/headings.ts
5649
+ var createHeadingGroupAction = (value) => createEditorActionGroup("heading", {
5650
+ variant: "dropdownMenu",
5651
+ applyActive: true,
5652
+ selectCardinality: "single",
5653
+ value
5654
+ }, "ph--text-h--regular");
5655
+ var createHeadingActions = (getView) => Object.entries({
5656
+ "0": "ph--paragraph--regular",
5657
+ "1": "ph--text-h-one--regular",
5658
+ "2": "ph--text-h-two--regular",
5659
+ "3": "ph--text-h-three--regular",
5660
+ "4": "ph--text-h-four--regular",
5661
+ "5": "ph--text-h-five--regular",
5662
+ "6": "ph--text-h-six--regular"
5663
+ }).map(([levelStr, icon]) => {
5664
+ const level = parseInt(levelStr);
5665
+ return createEditorAction(`heading--${levelStr}`, () => setHeading(level)(getView()), {
5666
+ label: [
5667
+ "heading level label",
5668
+ {
5669
+ count: level,
5670
+ ns: translationKey
5360
5671
  }
5361
- ]
5672
+ ],
5673
+ icon
5362
5674
  });
5675
+ });
5676
+ var computeHeadingValue = (state) => {
5677
+ const blockType = state ? state.blockType : "paragraph";
5678
+ const header = blockType && /heading(\d)/.exec(blockType);
5679
+ return header ? header[1] : blockType === "paragraph" || !blockType ? "0" : "";
5680
+ };
5681
+ var createHeadings = (state, getView) => {
5682
+ const headingValue = computeHeadingValue(state);
5683
+ const headingGroupAction = createHeadingGroupAction(headingValue);
5684
+ const headingActions = createHeadingActions(getView);
5685
+ return {
5686
+ nodes: [
5687
+ headingGroupAction,
5688
+ ...headingActions
5689
+ ],
5690
+ edges: [
5691
+ {
5692
+ source: "root",
5693
+ target: "heading"
5694
+ },
5695
+ ...headingActions.map(({ id }) => ({
5696
+ source: headingGroupAction.id,
5697
+ target: id
5698
+ }))
5699
+ ]
5700
+ };
5363
5701
  };
5364
5702
 
5365
- // packages/ui/react-ui-editor/src/extensions/modes.ts
5366
- import { keymap as keymap9 } from "@codemirror/view";
5367
- import { vim } from "@replit/codemirror-vim";
5368
- import { vscodeKeymap } from "@replit/codemirror-vscode-keymap";
5369
- import { S } from "@dxos/echo-schema";
5370
- var EditorViewModes = [
5371
- "preview",
5372
- "readonly",
5373
- "source"
5374
- ];
5375
- var EditorViewMode = S.Union(...EditorViewModes.map((mode) => S.Literal(mode)));
5376
- var EditorInputModes = [
5377
- "default",
5378
- "vim",
5379
- "vscode"
5380
- ];
5381
- var EditorInputMode = S.Union(...EditorInputModes.map((mode) => S.Literal(mode)));
5382
- var editorInputMode = singleValueFacet({});
5383
- var InputModeExtensions = {
5384
- default: [],
5385
- vscode: [
5386
- // https://github.com/replit/codemirror-vscode-keymap
5387
- editorInputMode.of({
5388
- type: "vscode"
5389
- }),
5390
- keymap9.of(vscodeKeymap)
5703
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/image.ts
5704
+ var createImageUploadAction = (onImageUpload) => createEditorAction("image", onImageUpload, {
5705
+ testId: "editor.toolbar.image",
5706
+ icon: "ph--image-square--regular"
5707
+ });
5708
+ var createImageUpload = (onImageUpload) => ({
5709
+ nodes: [
5710
+ createImageUploadAction(onImageUpload)
5391
5711
  ],
5392
- vim: [
5393
- // https://github.com/replit/codemirror-vim
5394
- vim(),
5395
- editorInputMode.of({
5396
- type: "vim",
5397
- noTabster: true
5398
- }),
5399
- keymap9.of([
5400
- {
5401
- key: "Alt-Escape",
5402
- run: (view) => {
5403
- view.dom.parentElement?.focus();
5404
- return true;
5405
- }
5406
- }
5407
- ])
5712
+ edges: [
5713
+ {
5714
+ source: "root",
5715
+ target: "image"
5716
+ }
5408
5717
  ]
5409
- };
5718
+ });
5410
5719
 
5411
- // packages/ui/react-ui-editor/src/extensions/preview/preview.ts
5412
- import "@dxos/lit-ui/dx-ref-tag.pcss";
5413
- import { syntaxTree as syntaxTree9 } from "@codemirror/language";
5414
- import { RangeSetBuilder as RangeSetBuilder4, StateField as StateField10 } from "@codemirror/state";
5415
- import { Decoration as Decoration8, EditorView as EditorView20, WidgetType as WidgetType6 } from "@codemirror/view";
5416
- var preview = (options = {}) => {
5417
- return [
5418
- // NOTE: Atomic block decorations must be created from a state field, now a widget, otherwise it results in the following error:
5419
- // "Block decorations may not be specified via plugins"
5420
- StateField10.define({
5421
- create: (state) => buildDecorations3(state, options),
5422
- update: (_, tr) => buildDecorations3(tr.state, options),
5423
- provide: (field) => [
5424
- EditorView20.decorations.from(field),
5425
- EditorView20.atomicRanges.of((view) => view.state.field(field))
5426
- ]
5427
- }),
5428
- EditorView20.theme({
5429
- ".cm-preview-block": {
5430
- marginLeft: "-1rem",
5431
- marginRight: "-1rem",
5432
- padding: "1rem",
5433
- borderRadius: "0.5rem",
5434
- background: "var(--dx-modalSurface)",
5435
- border: "1px solid var(--dx-separator)"
5436
- }
5437
- })
5438
- ];
5439
- };
5440
- var getLinkRef = (state, node) => {
5441
- const mark = node.getChild("LinkMark");
5442
- const label = node.getChild("LinkLabel");
5443
- if (mark && label) {
5444
- const ref = state.sliceDoc(label.from + 1, label.to - 1);
5445
- return {
5446
- suggest: ref.startsWith("?"),
5447
- block: state.sliceDoc(mark.from, mark.from + 1) === "!",
5448
- label: state.sliceDoc(mark.to, label.from - 1),
5449
- ref: ref.startsWith("?") ? ref.slice(1) : ref
5450
- };
5451
- }
5720
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/lists.ts
5721
+ var listStyles = {
5722
+ bullet: "ph--list-bullets--regular",
5723
+ ordered: "ph--list-numbers--regular",
5724
+ task: "ph--list-checks--regular"
5452
5725
  };
5453
- var buildDecorations3 = (state, options) => {
5454
- const builder = new RangeSetBuilder4();
5455
- syntaxTree9(state).iterate({
5456
- enter: (node) => {
5457
- switch (node.name) {
5458
- //
5459
- // Decoration.
5460
- // [Label][dxn:echo:123]
5461
- //
5462
- case "Link": {
5463
- const link = getLinkRef(state, node.node);
5464
- if (link) {
5465
- builder.add(node.from, node.to, Decoration8.replace({
5466
- widget: new PreviewInlineWidget(options, link)
5467
- }));
5468
- }
5469
- break;
5470
- }
5471
- //
5472
- // Block widget.
5473
- // ![Label][dxn:echo:123]
5474
- //
5475
- case "Image": {
5476
- const link = getLinkRef(state, node.node);
5477
- if (options.renderBlock && link) {
5478
- builder.add(node.from, node.to, Decoration8.replace({
5479
- block: true,
5480
- // atomic: true,
5481
- widget: new PreviewBlockWidget(options, link)
5482
- }));
5483
- }
5484
- break;
5485
- }
5486
- }
5726
+ var createListGroupAction = (value) => createEditorActionGroup("list", {
5727
+ variant: "toggleGroup",
5728
+ selectCardinality: "single",
5729
+ value
5730
+ });
5731
+ var createListActions = (value, getView) => Object.entries(listStyles).map(([listStyle, icon]) => {
5732
+ const checked = value === listStyle;
5733
+ return createEditorAction(`list-${listStyle}`, () => {
5734
+ const view = getView();
5735
+ if (!view) {
5736
+ return;
5737
+ }
5738
+ const listType = listStyle === "ordered" ? List.Ordered : listStyle === "bullet" ? List.Bullet : List.Task;
5739
+ if (checked) {
5740
+ removeList(listType)(view);
5741
+ } else {
5742
+ addList(listType)(view);
5743
+ }
5744
+ }, {
5745
+ checked,
5746
+ icon
5747
+ });
5748
+ });
5749
+ var createLists = (state, getView) => {
5750
+ const value = state.listStyle ?? "";
5751
+ const listGroupAction = createListGroupAction(value);
5752
+ const listActionsMap = createListActions(value, getView);
5753
+ return {
5754
+ nodes: [
5755
+ listGroupAction,
5756
+ ...listActionsMap
5757
+ ],
5758
+ edges: [
5759
+ {
5760
+ source: "root",
5761
+ target: "list"
5762
+ },
5763
+ ...listActionsMap.map(({ id }) => ({
5764
+ source: listGroupAction.id,
5765
+ target: id
5766
+ }))
5767
+ ]
5768
+ };
5769
+ };
5770
+
5771
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/search.ts
5772
+ import { openSearchPanel } from "@codemirror/search";
5773
+ var createSearchAction = (getView) => createEditorAction("search", () => openSearchPanel(getView()), {
5774
+ testId: "editor.toolbar.search",
5775
+ icon: "ph--magnifying-glass--regular"
5776
+ });
5777
+ var createSearch = (getView) => ({
5778
+ nodes: [
5779
+ createSearchAction(getView)
5780
+ ],
5781
+ edges: [
5782
+ {
5783
+ source: "root",
5784
+ target: "search"
5487
5785
  }
5786
+ ]
5787
+ });
5788
+
5789
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/view-mode.ts
5790
+ var createViewModeGroupAction = (value) => createEditorActionGroup("viewMode", {
5791
+ variant: "dropdownMenu",
5792
+ applyActive: true,
5793
+ selectCardinality: "single",
5794
+ value
5795
+ }, "ph--eye--regular");
5796
+ var createViewModeActions = (value, onViewModeChange) => Object.entries({
5797
+ preview: "ph--eye--regular",
5798
+ source: "ph--pencil-simple--regular",
5799
+ readonly: "ph--pencil-slash--regular"
5800
+ }).map(([viewMode, icon]) => {
5801
+ const checked = viewMode === value;
5802
+ return createEditorAction(`view-mode--${viewMode}`, () => onViewModeChange(viewMode), {
5803
+ label: [
5804
+ `${viewMode} mode label`,
5805
+ {
5806
+ ns: translationKey
5807
+ }
5808
+ ],
5809
+ checked,
5810
+ icon
5488
5811
  });
5489
- return builder.finish();
5812
+ });
5813
+ var createViewMode = (state, onViewModeChange) => {
5814
+ const value = state.viewMode ?? "source";
5815
+ const viewModeGroupAction = createViewModeGroupAction(value);
5816
+ const viewModeActions = createViewModeActions(value, onViewModeChange);
5817
+ return {
5818
+ nodes: [
5819
+ viewModeGroupAction,
5820
+ ...viewModeActions
5821
+ ],
5822
+ edges: [
5823
+ {
5824
+ source: "root",
5825
+ target: "viewMode"
5826
+ },
5827
+ ...viewModeActions.map(({ id }) => ({
5828
+ source: viewModeGroupAction.id,
5829
+ target: id
5830
+ }))
5831
+ ]
5832
+ };
5490
5833
  };
5491
- var PreviewInlineWidget = class extends WidgetType6 {
5492
- constructor(_options, _link) {
5493
- super();
5494
- this._options = _options;
5495
- this._link = _link;
5834
+
5835
+ // packages/ui/react-ui-editor/src/defaults.ts
5836
+ import { EditorView as EditorView21 } from "@codemirror/view";
5837
+ import { mx as mx4 } from "@dxos/react-ui-theme";
5838
+ var margin = "!mt-[1rem]";
5839
+ var editorWidth = "!mli-auto is-full max-is-[min(50rem,100%-4rem)]";
5840
+ var editorContent = mx4(margin, editorWidth);
5841
+ var editorFullWidth = mx4(margin);
5842
+ var editorGutter = EditorView21.theme({
5843
+ // Match margin from content.
5844
+ // Gutter = 2rem + 1rem margin.
5845
+ ".cm-gutters": {
5846
+ marginTop: "1rem",
5847
+ paddingRight: "1rem"
5496
5848
  }
5497
- // override ignoreEvent() {
5498
- // return false;
5499
- // }
5500
- eq(other) {
5501
- return this._link.ref === other._link.ref && this._link.label === other._link.label;
5849
+ });
5850
+ var editorMonospace = EditorView21.theme({
5851
+ ".cm-content": {
5852
+ fontFamily: fontMono
5502
5853
  }
5503
- toDOM(view) {
5504
- const root = document.createElement("dx-ref-tag");
5505
- root.textContent = this._link.label;
5506
- root.setAttribute("ref", this._link.ref);
5507
- return root;
5854
+ });
5855
+ var editorWithToolbarLayout = "grid grid-cols-1 grid-rows-[min-content_1fr] data-[toolbar=disabled]:grid-rows-[1fr] justify-center content-start overflow-hidden";
5856
+ var stackItemContentEditorClassNames = (role) => mx4("attention-surface dx-focus-ring-inset data-[toolbar=disabled]:pbs-2", role === "section" ? "[&_.cm-scroller]:overflow-hidden [&_.cm-scroller]:min-bs-24" : "min-bs-0");
5857
+ var stackItemContentToolbarClassNames = (role) => mx4("relative z-[1] flex is-full bg-toolbarSurface border-be border-separator", role === "section" && "sticky block-start-0 -mbe-px min-is-0");
5858
+
5859
+ // packages/ui/react-ui-editor/src/components/EditorToolbar/EditorToolbar.tsx
5860
+ var createToolbar = ({ getView, state, customActions, ...features }) => {
5861
+ const nodes = [];
5862
+ const edges = [];
5863
+ if (features.headings ?? true) {
5864
+ const headings2 = createHeadings(state, getView);
5865
+ nodes.push(...headings2.nodes);
5866
+ edges.push(...headings2.edges);
5508
5867
  }
5509
- };
5510
- var PreviewBlockWidget = class extends WidgetType6 {
5511
- constructor(_options, _link) {
5512
- super();
5513
- this._options = _options;
5514
- this._link = _link;
5868
+ if (features.formatting ?? true) {
5869
+ const formatting = createFormatting(state, getView);
5870
+ nodes.push(...formatting.nodes);
5871
+ edges.push(...formatting.edges);
5515
5872
  }
5516
- // override ignoreEvent() {
5517
- // return true;
5518
- // }
5519
- eq(other) {
5520
- return this._link.ref === other._link.ref;
5873
+ if (features.lists ?? true) {
5874
+ const lists = createLists(state, getView);
5875
+ nodes.push(...lists.nodes);
5876
+ edges.push(...lists.edges);
5521
5877
  }
5522
- toDOM(view) {
5523
- const root = document.createElement("div");
5524
- root.classList.add("cm-preview-block");
5525
- const handleAction = (action) => {
5526
- const pos = view.posAtDOM(root);
5527
- const node = syntaxTree9(view.state).resolve(pos + 1).node.parent;
5528
- if (!node) {
5529
- return;
5530
- }
5531
- const link = getLinkRef(view.state, node);
5532
- if (link?.ref !== action.link.ref) {
5533
- return;
5534
- }
5535
- switch (action.type) {
5536
- // TODO(burdon): Should we dispatch to the view or mutate the document? (i.e., handle externally?)
5537
- // Insert ref text.
5538
- case "insert": {
5539
- view.dispatch({
5540
- changes: {
5541
- from: node.from,
5542
- to: node.to,
5543
- insert: action.target.text
5544
- }
5545
- });
5546
- break;
5547
- }
5548
- // Remove ref.
5549
- case "delete": {
5550
- view.dispatch({
5551
- changes: {
5552
- from: node.from,
5553
- to: node.to
5554
- }
5555
- });
5556
- break;
5557
- }
5558
- }
5559
- };
5560
- this._options.renderBlock(root, {
5561
- readonly: view.state.readOnly,
5562
- link: this._link,
5563
- onAction: handleAction,
5564
- onLookup: this._options.onLookup
5565
- }, view);
5566
- return root;
5878
+ if (features.blocks ?? true) {
5879
+ const blocks = createBlocks(state, getView);
5880
+ nodes.push(...blocks.nodes);
5881
+ edges.push(...blocks.edges);
5567
5882
  }
5883
+ if (features.image) {
5884
+ const image2 = createImageUpload(features.image);
5885
+ nodes.push(...image2.nodes);
5886
+ edges.push(...image2.edges);
5887
+ }
5888
+ if (customActions) {
5889
+ const custom = customActions();
5890
+ nodes.push(...custom.nodes);
5891
+ edges.push(...custom.edges);
5892
+ }
5893
+ const editorToolbarGap = createGapSeparator();
5894
+ nodes.push(...editorToolbarGap.nodes);
5895
+ edges.push(...editorToolbarGap.edges);
5896
+ if (features.comment) {
5897
+ const comment = createComment2(state, getView);
5898
+ nodes.push(...comment.nodes);
5899
+ edges.push(...comment.edges);
5900
+ }
5901
+ if (features.search ?? true) {
5902
+ const search = createSearch(getView);
5903
+ nodes.push(...search.nodes);
5904
+ edges.push(...search.edges);
5905
+ }
5906
+ if (features.viewMode) {
5907
+ const viewMode = createViewMode(state, features.viewMode);
5908
+ nodes.push(...viewMode.nodes);
5909
+ edges.push(...viewMode.edges);
5910
+ }
5911
+ return {
5912
+ nodes,
5913
+ edges
5914
+ };
5568
5915
  };
5569
-
5570
- // packages/ui/react-ui-editor/src/extensions/typewriter.ts
5571
- import { keymap as keymap10 } from "@codemirror/view";
5572
- var defaultItems = [
5573
- "hello world!",
5574
- "this is a test.",
5575
- "this is [DXOS](https://dxos.org)"
5576
- ];
5577
- var typewriter = ({ delay = 75, items = defaultItems } = {}) => {
5578
- let t;
5579
- let idx = 0;
5580
- return [
5581
- keymap10.of([
5582
- {
5583
- // Reset.
5584
- key: "alt-meta-'",
5585
- run: (view) => {
5586
- clearTimeout(t);
5587
- idx = 0;
5588
- return true;
5589
- }
5590
- },
5591
- {
5592
- // Next prompt.
5593
- // TODO(burdon): Press 1-9 to select prompt?
5594
- key: "shift-meta-'",
5595
- run: (view) => {
5596
- clearTimeout(t);
5597
- const text = items[idx++];
5598
- if (idx === items?.length) {
5599
- idx = 0;
5600
- }
5601
- let i = 0;
5602
- const insert = (d = 0) => {
5603
- t = setTimeout(() => {
5604
- const pos = view.state.selection.main.head;
5605
- view.dispatch({
5606
- changes: {
5607
- from: pos,
5608
- insert: text[i++]
5609
- },
5610
- selection: {
5611
- anchor: pos + 1
5612
- }
5613
- });
5614
- if (i < text.length) {
5615
- insert(Math.random() * delay * (text[i] === " " ? 2 : 1));
5616
- }
5617
- }, d);
5618
- };
5619
- insert();
5620
- return true;
5621
- }
5622
- }
5623
- ])
5624
- ];
5625
- };
5626
-
5627
- // packages/ui/react-ui-editor/src/hooks/useActionHandler.ts
5628
- import { useCallback as useCallback2 } from "react";
5629
- var useActionHandler = (view) => {
5630
- return useCallback2((action) => view && processEditorPayload(view, action.properties), [
5631
- view
5916
+ var useEditorToolbarActionGraph = (props) => {
5917
+ const menuCreator = useCallback(() => createToolbar(props), [
5918
+ props
5632
5919
  ]);
5920
+ return useMenuActions(menuCreator);
5633
5921
  };
5922
+ var EditorToolbar = /* @__PURE__ */ memo(({ classNames, attendableId, role, ...props }) => {
5923
+ const menuProps = useEditorToolbarActionGraph(props);
5924
+ return /* @__PURE__ */ React3.createElement("div", {
5925
+ role: "none",
5926
+ className: stackItemContentToolbarClassNames(role)
5927
+ }, /* @__PURE__ */ React3.createElement(ElevationProvider, {
5928
+ elevation: role === "section" ? "positioned" : "base"
5929
+ }, /* @__PURE__ */ React3.createElement(MenuProvider, {
5930
+ ...menuProps,
5931
+ attendableId
5932
+ }, /* @__PURE__ */ React3.createElement(ToolbarMenu, {
5933
+ classNames: [
5934
+ textBlockWidth,
5935
+ classNames
5936
+ ]
5937
+ }))));
5938
+ });
5634
5939
 
5635
5940
  // packages/ui/react-ui-editor/src/hooks/useTextEditor.ts
5636
- import { EditorState as EditorState2 } from "@codemirror/state";
5637
- import { EditorView as EditorView21 } from "@codemirror/view";
5941
+ import { EditorState as EditorState3 } from "@codemirror/state";
5942
+ import { EditorView as EditorView22 } from "@codemirror/view";
5638
5943
  import { useFocusableGroup } from "@fluentui/react-tabster";
5639
- import { useCallback as useCallback3, useEffect as useEffect2, useMemo as useMemo4, useRef, useState } from "react";
5640
- import { log as log7 } from "@dxos/log";
5944
+ import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo4, useRef, useState } from "react";
5945
+ import { log as log8 } from "@dxos/log";
5641
5946
  import { getProviderValue, isNotFalsy as isNotFalsy4 } from "@dxos/util";
5642
- var __dxlog_file11 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5947
+ var __dxlog_file12 = "/home/runner/work/dxos/dxos/packages/ui/react-ui-editor/src/hooks/useTextEditor.ts";
5643
5948
  var instanceCount = 0;
5644
5949
  var useTextEditor = (props = {}, deps = []) => {
5645
5950
  const { id, doc, initialValue, extensions, autoFocus, scrollTo, selection, moveToEndOfLine, debug } = useMemo4(() => getProviderValue(props), deps ?? []);
@@ -5649,12 +5954,12 @@ var useTextEditor = (props = {}, deps = []) => {
5649
5954
  useEffect2(() => {
5650
5955
  let view2;
5651
5956
  if (parentRef.current) {
5652
- log7("create", {
5957
+ log8("create", {
5653
5958
  id,
5654
5959
  instanceId,
5655
5960
  doc: initialValue?.length ?? 0
5656
5961
  }, {
5657
- F: __dxlog_file11,
5962
+ F: __dxlog_file12,
5658
5963
  L: 76,
5659
5964
  S: void 0,
5660
5965
  C: (f, a) => f(...a)
@@ -5671,16 +5976,16 @@ var useTextEditor = (props = {}, deps = []) => {
5671
5976
  anchor
5672
5977
  };
5673
5978
  }
5674
- const state = EditorState2.create({
5979
+ const state = EditorState3.create({
5675
5980
  doc: doc ?? initialValue,
5676
5981
  // selection: initialSelection,
5677
5982
  extensions: [
5678
5983
  id && documentId.of(id),
5679
5984
  extensions,
5680
5985
  // NOTE: This doesn't catch errors in keymap functions.
5681
- EditorView21.exceptionSink.of((err) => {
5682
- log7.catch(err, void 0, {
5683
- F: __dxlog_file11,
5986
+ EditorView22.exceptionSink.of((err) => {
5987
+ log8.catch(err, void 0, {
5988
+ F: __dxlog_file12,
5684
5989
  L: 98,
5685
5990
  S: void 0,
5686
5991
  C: (f, a) => f(...a)
@@ -5688,10 +5993,10 @@ var useTextEditor = (props = {}, deps = []) => {
5688
5993
  })
5689
5994
  ].filter(isNotFalsy4)
5690
5995
  });
5691
- view2 = new EditorView21({
5996
+ view2 = new EditorView22({
5692
5997
  parent: parentRef.current,
5693
5998
  state,
5694
- scrollTo: scrollTo ? EditorView21.scrollIntoView(scrollTo, {
5999
+ scrollTo: scrollTo ? EditorView22.scrollIntoView(scrollTo, {
5695
6000
  yMargin: 96
5696
6001
  }) : void 0,
5697
6002
  dispatchTransactions: debug ? debugDispatcher : void 0
@@ -5709,10 +6014,10 @@ var useTextEditor = (props = {}, deps = []) => {
5709
6014
  setView(view2);
5710
6015
  }
5711
6016
  return () => {
5712
- log7("destroy", {
6017
+ log8("destroy", {
5713
6018
  id
5714
6019
  }, {
5715
- F: __dxlog_file11,
6020
+ F: __dxlog_file12,
5716
6021
  L: 135,
5717
6022
  S: void 0,
5718
6023
  C: (f, a) => f(...a)
@@ -5724,12 +6029,12 @@ var useTextEditor = (props = {}, deps = []) => {
5724
6029
  if (view) {
5725
6030
  if (scrollTo || selection) {
5726
6031
  if (selection && selection.anchor > view.state.doc.length) {
5727
- log7.warn("invalid selection", {
6032
+ log8.warn("invalid selection", {
5728
6033
  length: view.state.doc.length,
5729
6034
  scrollTo,
5730
6035
  selection
5731
6036
  }, {
5732
- F: __dxlog_file11,
6037
+ F: __dxlog_file12,
5733
6038
  L: 144,
5734
6039
  S: void 0,
5735
6040
  C: (f, a) => f(...a)
@@ -5761,7 +6066,7 @@ var useTextEditor = (props = {}, deps = []) => {
5761
6066
  Escape: view?.state.facet(editorInputMode).noTabster
5762
6067
  }
5763
6068
  });
5764
- const handleKeyUp = useCallback3((event) => {
6069
+ const handleKeyUp = useCallback2((event) => {
5765
6070
  const { key, target, currentTarget } = event;
5766
6071
  if (target === currentTarget) {
5767
6072
  switch (key) {
@@ -5788,9 +6093,9 @@ export {
5788
6093
  Cursor,
5789
6094
  EditorInputMode,
5790
6095
  EditorInputModes,
5791
- EditorState3 as EditorState,
6096
+ EditorState4 as EditorState,
5792
6097
  EditorToolbar,
5793
- EditorView22 as EditorView,
6098
+ EditorView23 as EditorView,
5794
6099
  EditorViewMode,
5795
6100
  EditorViewModes,
5796
6101
  Inline,
@@ -5820,7 +6125,7 @@ export {
5820
6125
  commentsState,
5821
6126
  convertTreeToJson,
5822
6127
  createBasicExtensions,
5823
- createComment2 as createComment,
6128
+ createComment,
5824
6129
  createDataExtensions,
5825
6130
  createEditorAction,
5826
6131
  createEditorActionGroup,
@@ -5864,6 +6169,7 @@ export {
5864
6169
  mention,
5865
6170
  openCommand,
5866
6171
  openEffect,
6172
+ outliner,
5867
6173
  overlap,
5868
6174
  preventNewline,
5869
6175
  preview,
@@ -5898,7 +6204,6 @@ export {
5898
6204
  toggleStyle,
5899
6205
  translations_default as translations,
5900
6206
  typewriter,
5901
- useActionHandler,
5902
6207
  useCommentClickListener,
5903
6208
  useCommentState,
5904
6209
  useComments,